EmDash & Astro
EmDash is an open-source, full-stack CMS built specifically for Astro, adding database-backed content, an admin UI, a media library, menus, and taxonomies to your site.
Integrating with Astro
Section titled “Integrating with Astro”EmDash runs within your Astro project and relies on a database. You can access the admin interface at /_emdash/admin to edit content. Your pages will display it by querying the database using live content collections.
This guide uses the Node.js adapter and a local SQLite database. See the EmDash docs for a list of supported databases.
Prerequisites
Section titled “Prerequisites”- An existing Astro project (Astro 6 or later) configured with an adapter and
output: "server". - Node.js v22.16.0 or higher.
Installing dependencies
Section titled “Installing dependencies”React powers the EmDash admin interface and is a required dependency. If your project does not use React, install it using the astro add command for your package manager:
npx astro add reactpnpm astro add reactyarn astro add reactYou also need to install the EmDash package:
npm install emdashpnpm add emdashyarn add emdashAdding the integration
Section titled “Adding the integration”Add the emdash() integration to your Astro config file, and configure a database and a media storage backend:
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", }), }), ],});Adding the live collections loader
Section titled “Adding the live collections loader”Create a src/live.config.ts file so that Astro’s content layer can resolve EmDash content:
import { defineLiveCollection } from "astro:content";import { emdashLoader } from "emdash/runtime";
export const collections = { _emdash: defineLiveCollection({ loader: emdashLoader() }),};The _emdash collection internally routes to your content types (e.g. posts and pages). Any existing file-based collections in src/content.config.ts keep working alongside it.
Running EmDash locally
Section titled “Running EmDash locally”EmDash requires you to complete the setup wizard before testing your own pages. Until the setup is complete, there is no published content and queries return empty results.
-
Start Astro’s dev server to initialize the database and launch the admin UI:
Terminal window npm run devTerminal window pnpm run devTerminal window yarn run devOn first run, EmDash creates
data.dbwith its schema and two default collections,pagesandposts. -
Visit
http://localhost:4321/_emdash/adminin the browser. EmDash redirects you to the setup wizard. -
In the Site step, enter a site title and an optional tagline.
-
In the Account step, enter your email address and name. This creates the administrator account.
-
In the Sign In step, secure your account. Choose Create Passkey to register a passkey with your device’s biometric authentication, security key, or PIN.
-
Sign in with your new passkey to reach the dashboard.
Creating your first post
Section titled “Creating your first post”-
In the dashboard, click the + Post button.
-
Add a title and some content. EmDash stores rich text as Portable Text, edited in a block editor. A URL slug is generated from the title and can be edited in the sidebar.
-
Click Save, then Publish. Only published posts are visible to site visitors.
Rendering EmDash content
Section titled “Rendering EmDash content”Query your content with getEmDashCollection() and getEmDashEntry(). Both follow the live collections pattern and return results at request time, so published changes appear without a rebuild.
Displaying a list of posts
Section titled “Displaying a list of posts”The following example displays a list of all published post titles, each linking to an individual post page:
---import { getEmDashCollection } from "emdash";
const { entries: posts } = await getEmDashCollection("posts", { status: "published",});---<ul> {posts.map((post) => ( <li> <a href={`/posts/${post.data.slug}`}>{post.data.title}</a> </li> ))}</ul>Displaying a single post
Section titled “Displaying a single post”To display content from an individual post, fetch it by its slug and render the Portable Text content with the <PortableText /> component:
---import { getEmDashEntry } from "emdash";import { PortableText } from "emdash/ui";
const { slug } = Astro.params;const { entry: post } = await getEmDashEntry("posts", slug);
if (!post) { return Astro.redirect("/404");}---<article> <h1>{post.data.title}</h1> <PortableText value={post.data.content} /></article>See the EmDash querying guide for more information on filtering, pagination, previewing drafts, and visual editing.
Deploying EmDash + Astro
Section titled “Deploying EmDash + Astro”EmDash deploys together with your site as a single Astro project. Choose a host that supports your adapter, and provision a production database and media storage.
See the EmDash deployment guide for Node.js and the EmDash deployment guide for Cloudflare for specific instructions. You can also visit Astro’s deployment guides and follow the instructions to deploy with your preferred hosting provider.
Official Resources
Section titled “Official Resources”More CMS guides
Featured CMS partners
-
CloudCannon
Git-based CMS built for speed, security, and zero headaches.