Skip to content

Architecture

EmDash is an Astro integration. You add it to astro.config.mjs, choose a database and storage, and define content collections. This page covers the model you need to build a site. For internals (table layouts, the request path, code generation), see Architecture (internals).

┌──────────────────────────────────────────────┐
│ Your Astro site │
│ │
│ Pages and components you write │
│ │ │
│ │ getEmDashCollection() / getEmDashEntry()
│ ▼ │
│ ┌───────────────┐ ┌────────────────┐ │
│ │ Content │ │ Admin panel │ │
│ │ (your data) │◄────►│ /_emdash/admin │ │
│ └───────────────┘ └────────────────┘ │
│ │ │
│ Database (SQLite / libSQL / D1 / Postgres) │
│ Media storage (local / R2 / S3) │
└──────────────────────────────────────────────┘

You write pages and components as normal. EmDash provides the content, an admin panel for editing it, and the database and storage behind it. Editors work in the admin panel; your pages read the same content through query functions.

You define collections and fields — in the admin panel or with the CLI — and EmDash stores content against them.

Change it anytime

Add, rename, remove, or retype collections and fields whenever you need to. Changes take effect immediately.

Editor-designed

A content editor can design the whole model through the admin UI.

Typed

Generate TypeScript types from the current model for autocomplete from query to template.

Portable

Export the model as a JSON seed file for version control, and apply it in another environment.

See the content model for how to define, change, type, and seed it.

EmDash serves content through Astro’s Live Collections at runtime, so changes an editor makes are visible immediately. You read content with two functions:

src/pages/blog.astro
import { getEmDashCollection, getEmDashEntry } from "emdash";
const { entries: posts } = await getEmDashCollection("posts");
const { entry: post } = await getEmDashEntry("posts", "my-post-slug");

See Querying content for filtering, pagination, and drafts.

You pass a database and a storage backend to the integration. Everything else has a default.

astro.config.mjs
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({ directory: "./uploads" }),
}),
],
});
  • Database: SQLite (local or libSQL), Cloudflare D1, or PostgreSQL. See Database options.
  • Storage: the local filesystem, Cloudflare R2, or any S3-compatible storage for media. See Storage options.

Plugins react to content and media lifecycle events and can add admin pages, dashboard widgets, and settings. There are two formats:

  • Native plugins run in the host environment with full access. Best for first-party and trusted plugins.
  • Sandboxed plugins run in an isolated runtime with capability-based permissions. Best for third-party plugins.

See the plugin overview to choose and install plugins, or create a plugin.

Admin Panel

See what the admin panel offers editors and admins.