Skip to content

Getting started ​

What you'll learn

Add Easy CMS to a Nuxt or Next.js project (or run it on its own), create the first admin and read content in a page.

Before this page: What is Easy CMS?.

The admin dashboard after logging in

You need a Nuxt 4 or Next.js 15+ project and Node.js ≥ 22.12. Using another framework, or none? Run Easy CMS as a standalone server.

Add Easy CMS ​

In your project directory:

bash
npx create-easy-cms
bash
pnpm create easy-cms
bash
yarn create easy-cms
bash
bun create easy-cms

It detects Nuxt or Next.js, asks which database to use, installs the packages and:

  • creates easy-cms.config.ts with a sample posts collection and a site global
  • adds a random EASY_CMS_SECRET to .env
  • ignores cms.db, uploads/ and .pglite/ in .gitignore
  • Nuxt: adds @easy-cms/nuxt to modules
  • Next.js: creates app/api/cms/[[...path]]/route.ts and app/admin/[[...path]]/route.ts and wraps next.config.ts in withEasyCMS()

Options: --db sqlite|postgres, --pm npm|pnpm|yarn|bun, --yes (accept defaults), --skip-install. Running it again is safe. Commands on these pages have a tab for each package manager; the one you pick is kept.

Run it ​

bash
npm run dev
bash
pnpm dev
bash
yarn dev
bash
bun run dev

Open http://localhost:3000/admin. With no users yet, the admin asks you to create the first admin. You can also run npx easy-cms create-admin.

In development the database schema follows your config automatically. Edit easy-cms.config.ts, save, and the admin shows the new fields.

Read content ​

ts
export default defineEventHandler(async () => {
  const cms = await useEasyCMS()
  return cms.find('posts', { sort: '-createdAt', limit: 10 })
})
tsx
import { getEasyCMS } from '@easy-cms/next'
import config from '@/easy-cms.config'

export const dynamic = 'force-dynamic'

export default async function Home() {
  const cms = await getEasyCMS(config)
  const { docs } = await cms.find('posts', { sort: '-createdAt', limit: 10 })
  return <ul>{docs.map((post) => <li key={post.id}>{post.title}</li>)}</ul>
}

find returns only published documents by default, and post.title is typed as string because the config says the field is required text.

Next steps ​

Released under the MIT License.