Since 2020, I’ve been doing much of my thinking, writing, and note taking in Obsidian, a Markdown editor that supports wiki-style links and has a robust plugin ecosystem. This website is built using Astro, which can create pages or posts from Markdown files. The process of manually copying and reformatting stuff from Obsidian to Astro is time consuming and error prone enough that I decided to build myself a little Obsidian plugin to reduce that friction. (Plus, programming is kinda fun sometimes.) It took about a week to get it into a really useful state, and I’m calling it Obsidian Courier.
Since Courier is written specifically for my particular workflow, I’m not planning to officially release it as a community plugin. I may in the future if there’s interest.
The plugin adds a command to Obsidian that takes the current file, does some stuff to it, and copies it to my local Astro content directory. At that point, I can preview it in Astro, commit it to git, and push to deploy.
Add a title to frontmatter: Ensures my post has a title, using the filename if necessary.
Example:
---
date: 2024-08-03
---
---
date: 2024-08-03
title: My cool post
---
Image processing: The plugin takes any images embedded in the document and copies them to Astro, putting them into a directory named for the post.
Here’s how it works:
/content/[collection]/[post-slug]/
)Example:
# My cool post
Here's a diagram explaining the concept:
![[cool diagram.png]]
this becomes
import { Image } from 'astro:assets';
import coolDiagram from './cool-diagram.png';
# My cool post
Here's a diagram explaining the concept:
<Image src={coolDiagram} alt="" />
type
) to determine the appropriate Astro content collection for the post. There’s a setting for the default collection that’s used if the frontmatter doesn’t have a type
.If I have a note with type: "media"
in its frontmatter, the plugin will output it to /content/media/my-post.mdx
. This corresponds to Astro’s content collection structure, where each subdirectory under /content
represents a collection.
Here’s an example of how my media
collection is defined in content/config.ts
:
import { defineCollection, z } from 'astro:content';
const media = defineCollection({
type: 'content',
schema: z.object({
category: z.enum(["Movie", "TV"]),
date: z.date(),
link: z.string().optional(),
title: z.string(),
titleTranslated: z.string().optional(),
yearPublished: z.number(),
}),
});
export const collections = {
"media": media,
// ... other collections
};
Remove wikilinks: It automatically converts Obsidian’s wikilink format (link
) to normal text.
Example:
Check out [[My Cool Project]]
Check out My Cool Project
Remove private notes: I can put personal notes at the end of the file under a “Notes” heading and they’ll be removed.
Example:
Here's my public content.
## Notes
Remember to buy milk.
Here's my public content.
The code is available on GitHub.
Obsidian Courier is built using TypeScript and leverages Obsidian’s plugin API. Unit tests are written in Jest.
Here’s a high-level overview of the process:
addTitleToFrontmatter
: Ensures the post has a title, using the filename if necessaryImageCopier
: Copies images to a directory named for the post’s slugrewriteImages
: Converts image embeds to their respective import
and <Image />
linesremoveNotes
: Strips out any personal notes I don’t want to publishwikilinks
: Converts wiki-style links to plain textThis thing is already very useful for my needs, but some things I’m thinking about for the future:
Alt texts for images: Obsidian’s way of embedding images (![[Name of image]]
) doesn’t support alt text. I’m considering implementing a solution similar to the Obsidian Image Captions plugin to support alt text in a format like this: ![[Name of image|alt text]]
. For now, I’ll be manually adding the alt text once the file is in Astro.
Figure and figcaption support: I use figure and figcaption HTML elements quite often in my posts. I may develop a way to generate these without writing out the HTML, making it easier to add captions to images directly in Obsidian, although that would also require some non-standard Markdown.
Smart link conversion: Currently, the plugin removes all wikilinks to other documents. It would be cool to support links to documents that have already been published on my website. For example, [[Charlie Haden]]
would be converted to a proper link to that post on my site rather than being removed.
Process the entire vault: It might be nice to have a second command that looks through the entire vault for files that should be published. I’d probably make this an opt-in feature (e.g., by having publish: true
in frontmatter).