Files

4.2 KiB

Configuration

astro.config.mjs

Node.js (local development / self-hosted)

import node from '@astrojs/node';
import react from '@astrojs/react';
import { defineConfig } from 'astro/config';
import emdash, { local } from 'emdash/astro';
import { sqlite } from 'emdash/db';

export default defineConfig({
	output: 'server',
	adapter: node({ mode: 'standalone' }),
	image: {
		layout: 'constrained',
		responsiveStyles: true,
	},
	integrations: [
		react(),
		emdash({
			database: sqlite({ url: 'file:./data.db' }),
			storage: local({
				directory: './uploads',
				baseUrl: '/_emdash/api/media/file',
			}),
		}),
	],
	devToolbar: { enabled: false },
});

Cloudflare (D1 + R2)

import cloudflare from '@astrojs/cloudflare';
import react from '@astrojs/react';
import { d1, r2 } from '@emdash-cms/cloudflare';
import { defineConfig } from 'astro/config';
import emdash from 'emdash/astro';

export default defineConfig({
	output: 'server',
	adapter: cloudflare(),
	image: {
		layout: 'constrained',
		responsiveStyles: true,
	},
	integrations: [
		react(),
		emdash({
			database: d1({ binding: 'DB', session: 'auto' }),
			storage: r2({ binding: 'MEDIA' }),
		}),
	],
	devToolbar: { enabled: false },
});

Requires a wrangler.jsonc with D1 and R2 bindings:

{
	"name": "my-site",
	"compatibility_date": "2026-03-29",
	"compatibility_flags": ["nodejs_compat"],
	"assets": { "directory": "./dist" },
	"d1_databases": [
		{
			"binding": "DB",
			"database_name": "my-site",
 // from `wrangler d1 create my-site`
		},
	],
	"r2_buckets": [
		{
			"binding": "MEDIA",
			"bucket_name": "my-site-media",
		},
	],
}

Plugins

Register plugins in astro.config.mjs:

import { auditLogPlugin } from "@emdash-cms/plugin-audit-log";

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" }),
	plugins: [auditLogPlugin()],
}),

live.config.ts

Every EmDash site needs this file at src/live.config.ts. It's boilerplate -- the same in every project:

import { defineLiveCollection } from 'astro:content';
import { emdashLoader } from 'emdash/runtime';

export const collections = {
	_emdash: defineLiveCollection({ loader: emdashLoader() }),
};

This registers EmDash's live content collections with Astro. All content types are served through the single _emdash collection -- you query specific types using getEmDashCollection("posts") etc.

emdash-env.d.ts

Auto-generated at the project root when the dev server starts. Provides TypeScript types for your collections. This is the file your tsconfig.json includes.

/// <reference types="emdash/locals" />

import type { PortableTextBlock } from 'emdash';

export interface Post {
	id: string;
	slug: string | null;
	status: string;
	title: string;
	featured_image?: {
		id: string;
		src?: string;
		alt?: string;
		width?: number;
		height?: number;
	};
	content?: PortableTextBlock[];
	excerpt?: string;
	createdAt: Date;
	updatedAt: Date;
	publishedAt: Date | null;
}

declare module 'emdash' {
	interface EmDashCollections {
		posts: Post;
	}
}

The dev server regenerates this file automatically when schema changes. You can also generate it manually:

Type Generation

# From local dev server (writes emdash-env.d.ts at project root)
npx emdash types

# From remote instance
npx emdash types --url https://my-site.pages.dev

# Custom output path
npx emdash types --output src/types/cms.ts

The CLI also writes .emdash/schema.json with the raw schema for tooling.

package.json

Key dependencies for a Node.js site:

{
	"dependencies": {
		"astro": "^6.0.0",
		"emdash": "^0.0.3",
		"@astrojs/node": "^9.0.0",
		"@astrojs/react": "^4.0.0",
		"react": "^18.0.0",
		"react-dom": "^18.0.0"
	}
}

For Cloudflare, replace @astrojs/node with @astrojs/cloudflare and add @emdash-cms/cloudflare.

Dev Server

npx emdash dev              # Start dev server (runs migrations, applies seed)
npx emdash dev --types      # Start and generate types from schema

The admin UI is at http://localhost:4321/_emdash/admin. On first run, you'll go through setup to create an admin account.