Skip Navigation
Apr 10 2023

Configuring Obsidian and Astro Assets for Markdoc Content in an Astro Blog

Converting the blog to Astro was a more significant win than initially expected. A bit more time with the documentation of Astro, Obsidian, and Markdoc, and things went 100% green. On all counts.

Obsidian, a powerful and extensible knowledge for local plain-text files I previously wrote my first impressions after migrating the blog to Astro. As fast and easy as the migration was - a breath of fresh air, really - I couldn’t achieve all I wanted in the first go. A lot of that had to do with the first-contact situation.

A few days later, I learned more about the new stack. And things got waaaay better.


My initial conclusions on the setup I wanted were:

  • a performant output without days of optimization ✅
  • privacy-first setup, including analytics ✅
  • relatively well-integrated tooling - 😞 I WANT Markdoc in .md files. I really do.
  • easy authoring ✅ - I may not yet do all I could do with Markdoc while preserving Obsidian sweetness, but my authoring workflow is much improved thanks to general fixes. Still, some to go, but I’m happy for now.
  • growth opportunity - 🙏 I don’t want to go MDX. Just don’t like it. While Markdoc isn’t a real option, I can’t see the blog going in the direction I envisioned.

In the days following the conversion, I familiarized myself with Astro’s ecosystem. I tried multiple integrations and roamed their GitHub repositories and Discord channels, ingesting information. And I tested more Markdoc with Obsidian.

As a result, despite initially converting all documents to .md, today, I proudly present content authored in Markdoc! And I take back what I said about wanting the Astro Markdoc integration to work in .md files. It actually turned out to be an advantage when I was working on things. I can imagine it is a huge feature for those needing to incrementally switch to Markdoc.

So, what were my issues:

  • I had not switched to using Astro’s Assets feature. It’s still experimental. But since I’m going Markdoc, I might as well use Assets! It is much easier to work with images now.
  • The plugin I use in Obsidian that helps me autocomplete is choppy at best. Tag autocompletion still not solved.
  • I wanted a custom Shiki theme for the code blocks. With some work, I managed to get parity between the .md and .mdoc code blocks.

I now have the following:

  • a performant output without days of optimization ✅
  • privacy-first setup, including analytics ✅
  • relatively well-integrated tooling ✅
  • easy authoring ✅
  • growth opportunity ✅

🎊 🥳

If you want to write your content for your Astro blog in Obsidian, keep reading.

Setting up a seamless images integration

With images in the public/images folder, I could not properly use images in Obsidian. I could not put the image link directly to the public folder in my documents. In the past, I created a symlink of the public/images folder in my Obsidian vault for similar situations. That worked well enough, but using Obsidian with symlinks has some caveats. So I needed a better solution.

After reading the Astro Assets documentation, I started migrating the blog assets because, following the migration, the content and the images would share a path from the project’s root. Astro takes care of the rest.

Move the assets

Move the blog images from public to src/assets. Mine were stored in public/images/uploads and now reside in src/assets/images

Enable the experimental Assets feature

Just moving the images to another folder isn’t sufficient. You need to update some files as follows:

  1. In the root of the Astro project, in the astro.config.mjs file, remove the existing @astrojs/image integration, and add the following:
    export default defineConfig({
      // other props
      experimental: {
        assets: true,
      // add this if you are using sharp
      image: {
        service: "astro/assets/services/sharp",
      // ...integrations, etc. 
  2. Update your tsconfig.json > types or env.d.ts file from using astro/clienttypes to usingastro/client-image` types.
  3. In the src/content folder, update the content schema defined in the config.mjs file (other whatever extension you use), such that the schema for a content collection is a function with parameters. For example:
    // ./src/content/config.ts#L5-L16
    const postsCollection = defineCollection({
      schema: ({ image }) => z.object({
        title: z.string(),
        description: z.string(),
        image: image().optional(),
        imageAlt: z.string().optional(),
        tags: z.array(z.string()),
        draft: z.boolean(),
    NOTE: The collection schema is now a function! Here I don’t use validation on the image. If you add validation, add chain .optional() after the validation. If you always require images, you can omit .optional() to get an error when the image is missing. See an example with validation in the Astro Assets documentation.
  4. Update all Astro components to use the Assets Image component instead of the @astrojs/image integration component:
    // replace
    // import {Image} from "@astrojs/image/components";
    // with:
    import { Image } from "astro:assets";
  5. Finally, update all uses of the Image component:
      alt={} />
    NOTE: You can remove width and height properties. The Astro Assets Image component takes care of it. What a joy!

Fix the content

With all the elements in place, it’s time to address the images in the content files.

For every file with images:

  1. Update the frontmatter data according to the updated content schema. For example:
    image: ../../assets/medicinal-plants-start.webp
    imageAlt: Medicinal plants in bowls
    NOTE: My posts folder is src/content/posts, so I need to go two levels up and grab the image from /src/assets. Astro’s Assets Image component automatically resolves image properties such as width and height, simplifying frontmatter image data. My previous image frontmatter was:
      src: /images/uploads/medicinal-plants-start.webp
      alt: Medicinal plants in bowls
      width: 506 # manual insert, yuck!
      height: 667 # manual insert, more yuck!
  2. Update image elements in the markdown content to use the relative path to src/assets:
    ![Some alt text.](../../{path-to-image})

At this point, I could see the images on the pages and when editing in Obsidian without the symlink setup. Lovely.

But there’s one more thing I would like. Obsidian supports dragging and dropping images in the current document instead of typing the image path and generating the image link. Correct image link resolution requires a bit of Obsidian configuration.

Setting up Obsidian

For a good workflow with Obsidian and Astro Markdoc, we need:

  • .mdoc file support.
  • A clean file tree.
  • Templates.
  • Tag autocompletion/suggestions from existing tags.
  • An advanced language & grammar checker.
  • Drag and drop images in the content, with the correct path resolution to the src/assets folder.

Set up the Obsidian vault

A vault is a folder on your local file system where Obsidian stores your notes.

The best place for the Obsidian vault appears to be in the src folder of the Astro project for the following reasons:

  • Astro doesn’t like the templates for Obsidian in the src/content folder.
  • Obsidian needs those templates readily available to help you scaffold new documents from templates.
  • For assets to work well, the Obsidian vault must be at the same level as the src/assets folder or above.
  • Less file tree hiding to do than if the vault was at the project’s root.

To set up the Obsidian vault:

  1. Follow the instructions for creating a vault from an existing folder. When browsing the file system, select the Astro project’s src folder.
  2. Enable community plugins.
  3. In the Obsidian Settings, in the Files & Links section, disable the Use [[Wikilinks]] setting (they might work, but I haven’t tested that).
  4. You might want to disable file extensions you don’t work with in Obsidian. For example, you can hide many files in the Astro project in Settings > Files & Links by adding this pattern to Excluded files:
    This helps keep the content graph view clean.

Add .mdoc file support

Obsidian can only open .md files by default, but there is a plugin for enabling additional file extensions.

To add support for .mdoc files:

  1. Go to the page for installing the Custom File Extensions and Types plugin by MeepTech. You will be prompted to open the plugin installation page in Obsidian.
  2. Install and enable the plugin.
  3. In the Obsidian Settings, find the Custom File Extensions Plugin options, and update the Config string to:

Clean up the file tree

When opening the vault, you can see all the files that are not excluded. Even if files are excluded, the folders remain in view.

I do not want that, as it clutters my brain, which I believe to be sufficiently cluttered without extra help. I want to see only those files and folders I need when working on content. I could use the

To clean up the file tree:

  1. Go to the page for installing the File Hider plugin by Oliver Atkins. You will be prompted to open the plugin installation page in Obsidian.
  2. Install and enable the plugin.
  3. Right-click every folder/file you don’t plan on using while writing and click Hide folder or Hide file.

If you want to keep files other than .md(oc) in the tree, in the lower-left corner, click the cog icon to open the Obsidian settings. In the Files & Links menu, enable the Detect all file extensions setting, adjust/remove the Excluded files pattern, and repeat step 5 for any files/folders you don’t want.

You can always re-add files/folders to the view by removing them from the hidden list in Settings > File Hider > Manage.

Set up content templates

To ensure an error-free frontmatter for Markdown & Markdoc files, you can create content templates based on the content schema.

To add templates to the project:

  1. Create a new folder in the vault, for example, _templates.
  2. In the Obsidian Settings, under the Templates section, set the Templates folder location to _templates and the desired date and time formats. I use YYYY-MM-DD and HH:mm:ss.
  3. In the _templates folder, create a post template matching the content schema:
    pubDate: {{date}}T{{time}}Z
    draft: true
  4. Create a new document and press Ctrl/Cmd + P. Search for template, and use the Templates: Insert template command. Select the new template. The document should now be populated with scaffolded frontmatter. Obsidian even fills in the current date and time if you used the date & time templates in the template frontmatter.

Add an advanced spelling and grammar checker

You can enhance the spelling and grammar checker capabilities of Obsidian by using one of the available plugins - Language Tools or Antidote. For these, you simply follow the installation procedure you’ve already tried.

However, I prefer Grammarly, which doesn’t have a plugin available in the Obsidian repository yet. The only option I know of is the [Obsidian Grammarly Plugin by Simon Pacis] (

To add Grammarly support to Obsidian:

This plugin is an incredible addition to the Markdown world. Grammarly has some kinks when it comes to text with “strange” in it (code, special characters, etc.), but it beats copy-pasting content from Markdown to something else and reformatting it when pasting it back in .md(oc).

TIP: There is also VS Code Grammarly extension if you need it.

Drag and drop images in the content

Dragging and dropping images automatically creates the correct image link syntax in Obsidian.

However, depending on your settings, the image source path may vary.

To have a correct image path when dragging and dropping the image:

  1. In the Obsidian Settings, in the Files & Links section, set the New Link Format to Relative path to file.
  2. Set the Attachment folder path to assets (or a subfolder of that, if you’re more organized than I am).

And done.

Further enhancements

Of course, these things by themselves don’t solve everything.

The Markdoc integration does not yet have full feature parity with the Remark/Rehype setup for Markdown & MDX.

See my Astro Markdoc solutions for linked headings, table of contents, handling links inside the Markdoc body, and adding Shiki support to the code blocks. Feel free to copy, enhance and share with the world 😁