Summit Themes
Blog

How to use Obsidian to write your Astro content (zero scripts)

Astro's content collections are a clean system for managing markdown files, but writing in a code editor is not for everyone. The moment a draft gets long, you want a focused writing environment — one without tabs full of component files. Obsidian fills that gap well. It is free, local-first, and natively built around .md files, which is exactly what Astro consumes. The setup takes about ten minutes and requires zero build scripts.

This guide covers the simplest working approach: point an Obsidian vault directly at your src/content folder, adjust three settings that would otherwise cause headaches, and add a frontmatter template so every new post starts with valid fields. That is the entire setup.

How the pieces fit together

Astro content collections work by reading .md (or .mdx) files from a folder, validating their frontmatter against a Zod schema, and exposing typed data to your components via getCollection(). Obsidian works by reading .md files from a vault folder and letting you edit them. The two systems overlap perfectly — they share the same file format. There is nothing to sync, export, or transpile. You write a file in Obsidian, save it, and Astro picks it up on the next build.

Step 1: open your Astro project as an Obsidian vault

Open Obsidian. Choose Open folder as vault and select your Astro project's root directory (the folder that contains src/, astro.config.mjs, etc.).

Obsidian will now show your entire project in the file explorer pane. That is fine — you will navigate to src/content/blog/ (or whichever collection you write for) when you create and open posts. All the other files sit there but you are not obligated to touch them.

One housekeeping step: add the Obsidian config folder to your .gitignore so it does not pollute your repository.

# .gitignore
.obsidian/

The .obsidian/ folder holds your personal editor preferences (theme, plugin settings, hotkeys). It has nothing to do with your site content and different team members will have different preferences, so keeping it out of git is the right call.

Step 2: make sure your content collection schema is defined

Before writing, confirm that Astro knows what frontmatter fields to expect. The config file lives at src/content.config.ts (note: not inside the content/ folder — it sits alongside it).

import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    draft: z.boolean().default(false),
    tags: z.array(z.string()).optional(),
  }),
});

export const collections = { blog };

The z.coerce.date() call is worth noting: it accepts a plain date string like 2026-06-22 from your frontmatter and converts it to a JavaScript Date object automatically. You do not need to format it in any special way in Obsidian.

Step 3: change three Obsidian settings

Out of the box, Obsidian uses a few conventions that do not play nicely with Astro. Go to Settings and make these changes.

Under Files & Links, disable Use [[Wikilinks]]. Wikilinks ([[Some Note]]) are an Obsidian-specific format. Astro does not understand them — they will appear as literal bracketed text in your rendered output. With this setting off, Obsidian writes standard Markdown links ([anchor text](./path.md)) instead, which Astro handles correctly.

Set a fixed attachments folder

Also under Files & Links, find Default location for new attachments and set it to In the folder specified below. Enter a path like src/content/blog/images (adjust to match your collection name).

This matters because when you paste or drag an image into an Obsidian note, it copies the file somewhere on disk. If you let Obsidian put it wherever it wants, images will end up scattered and the relative paths in your markdown will be unpredictable. A fixed folder keeps everything consistent.

In the same section, set New link format to Relative path from note. This ensures image and file links in your markdown use paths relative to the current file — exactly what Astro expects when it processes content collection entries.

Step 4: create a frontmatter template

The built-in Templates plugin (or the community Templater plugin for more power) lets you insert a pre-filled frontmatter block into every new post. This is the highest-leverage thing you can do: it prevents the most common content collection error, which is a file with missing or wrongly-typed frontmatter fields that breaks the Astro build.

Enable the core Templates plugin in Settings under Core plugins. Then create a new note at a path like _templates/blog-post.md with these contents:

---
title: 
description: 
pubDate: {{date:YYYY-MM-DD}}
draft: true
tags: []
---

The {{date:YYYY-MM-DD}} token is expanded by the Templates plugin when you insert the template, so pubDate is always populated with today's date in the correct ISO format that Astro's z.coerce.date() expects.

Configure the Templates plugin to look for templates in your _templates/ folder (Settings > Templates > Template folder location). Then create a new blog post file, open it, and use the command palette (Ctrl/Cmd+P) to run Templates: Insert template and choose your blog post template. The frontmatter is inserted instantly.

Add _templates/ to your .gitignore if you do not want template files committed, or leave them in — Astro will ignore files that do not match your glob pattern and do not have valid frontmatter, so they will not break anything either way.

Step 5: handle images properly

Images are the one area where "zero scripts" requires a small discipline. You have two clean options.

Option A: images inside src/content/blog/images/

Paste or drag images directly into an Obsidian note. Because you set the attachments folder to src/content/blog/images in Step 3, Obsidian copies the file there and writes a relative markdown image tag:

![Alt text](images/my-screenshot.jpg)

Astro's content collection image handling will pick these up and optimize them with Sharp during the build. In your schema, you can reference them with z.string() for a cover image path, or just use inline image syntax in the body — both work.

Option B: images in public/

Put images in public/images/ and reference them with absolute paths like /images/my-screenshot.jpg. They will not be Astro-optimized, but they are completely portable and Obsidian can still show them in preview mode if you configure the vault root correctly. This option is simpler if you do not care about Astro's image optimization pipeline.

Whichever option you choose, stay consistent. Mixing relative paths (for content-folder images) with absolute paths (for public images) in the same collection leads to broken image previews in Obsidian.

Writing workflow in practice

Once the setup is in place, the daily workflow is:

  1. Create a new note in src/content/blog/ (use Obsidian's file explorer or Ctrl/Cmd+N).
  2. Insert your blog post template via the command palette.
  3. Fill in the title and description, set draft: true, and start writing.
  4. When the post is ready to publish, change draft: false and commit the file.

Your getCollection() call can filter by draft status so unpublished posts never appear in production:

const posts = await getCollection('blog', ({ data }) => {
  return import.meta.env.PROD ? data.draft !== true : true;
});

This pattern lets you see drafts locally (npm run dev) but excludes them from the production build automatically.

What to watch out for

Obsidian's Properties panel. Newer versions of Obsidian show frontmatter as a visual Properties panel rather than raw YAML. This is generally fine — edits to the panel write valid YAML — but be careful with date fields. Obsidian may render a date property as a date-picker widget and store it in a format like 2026-06-22T00:00:00 (with a time component). Astro's z.coerce.date() handles that correctly, but it is worth checking your first few posts in source view to confirm the field looks like what you expect.

Do not symlink. An older approach involved creating symbolic links between an Obsidian vault elsewhere on disk and the Astro project's content folder. Obsidian officially discourages symlinks and they cause problems with cloud sync and file-watching. The vault-in-project approach described here is simpler and has no such caveats.

The .obsidian folder is local only. If you work across multiple machines, you will repeat the settings changes from Step 3 on each machine. That is a one-time five-minute task per machine, and it is worth it to keep editor preferences out of your repository.

That is the whole setup

No scripts, no sync daemons, no export plugins. You pointed Obsidian at src/content, disabled wikilinks, fixed where images land, and gave yourself a frontmatter template. Every file you save in Obsidian is a valid Astro content collection entry the moment you hit Ctrl+S. The build picks it up, the schema validates it, and TypeScript knows its shape. Obsidian handles the writing experience; Astro handles everything else.