npm create emdash@latest ships a pre-wired project, but EmDash also grafts onto an Astro site you already have. This guide walks through every requirement the starter template normally handles for you — each one produces a confusing failure when it’s missing, so work through the checklist in order.
Prerequisites
- Astro 6 or later — upgrade first if you’re on an older major (
npx @astrojs/upgrade) - Node.js v22.12.0 or higher (odd-numbered versions are not supported)
- Server output — EmDash serves content at runtime, so your project needs
output: "server"and an adapter (Node, Cloudflare, …)
Install the Packages
Install EmDash together with its required peer dependencies. React powers the admin UI at /_emdash/admin; it is required even if your site itself doesn’t use React.
npm
npm install emdash @astrojs/react react react-dom pnpm
pnpm add emdash @astrojs/react react react-dom yarn
yarn add emdash @astrojs/react react react-dom For a local SQLite database (the default for development), also install the driver:
npm install better-sqlite3
Deploying to Cloudflare? Add the Cloudflare packages as well — the Deploy to Cloudflare guide covers them in detail:
npm install @astrojs/cloudflare @emdash-cms/cloudflare
Register the Integrations
Add both react() and emdash() to your integrations array. Registering @astrojs/react is not optional: installing the package alone is not enough, and without the integration the admin builds fine but never hydrates — the page sits on “Loading EmDash…” forever.
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
import react from "@astrojs/react";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
output: "server",
adapter: node({ mode: "standalone" }),
integrations: [
react(),
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
Add the Live Collections Loader
Create src/live.config.ts so Astro’s content layer can resolve EmDash content. Without it, getEmDashCollection / getEmDashEntry have no live collection to route through.
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
Your existing src/content.config.ts (file-based collections) keeps working alongside it — see EmDash for Astro Developers for how the two coexist.
Verify the Install
-
Start the dev server:
npm run dev -
Open
http://localhost:4321/_emdash/adminand complete the Setup Wizard. -
Create and publish a post, then query it from a page:
--- import { getEmDashCollection } from "emdash"; const { entries: posts } = await getEmDashCollection("posts", { status: "published", }); --- <ul>{posts.map((post) => <li>{post.data.title}</li>)}</ul>
Deploying to Cloudflare
Follow Deploy to Cloudflare for the full setup (D1 database, R2 media bucket, cron triggers). Two things trip up existing projects in particular:
- Use Cloudflare Workers, not Pages. The
@astrojs/cloudflareadapter emits awrangler.jsonthat Pages does not accept. If your site currently deploys to Pages, migrate it to Workers first. - Bindings must exist in your
wrangler.jsonc. At minimum you need a D1 binding for the database and an R2 binding for media, matching the binding names in yourastro.config.mjs.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Admin stuck on “Loading EmDash…” | @astrojs/react not registered | Add react() to integrations |
Could not resolve "astro:content" in live.config.ts | Astro older than 6 | Upgrade Astro |
getEmDashCollection returns an error | Missing src/live.config.ts | Add the live collections loader |
| Build errors about unresolved packages | Peer dependencies not installed | Install @astrojs/react, react, react-dom explicitly |
| Content changes don’t appear | Page is prerendered | Set export const prerender = false on dynamic pages |
Next Steps
- EmDash for Astro Developers — How EmDash concepts map onto what you already know
- Working with Content — Query and render content
- Deploy to Cloudflare — Take the site to production