# Getting started

::: info 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?](./what-is-easy-cms).
:::

<Screenshot name="dashboard" alt="The admin dashboard after logging in" />

You need a Nuxt 4 or Next.js 15+ project and Node.js 22.12 or newer (22.19 or newer with Nuxt). Using another framework, or none?
Run Easy CMS as a [standalone server](./standalone).

::: warning npm 9 can't create a Nuxt app
Check `npm --version` first: with npm 9, as Ubuntu 26.04's own `nodejs` package has (npm 9.2),
`npm create nuxt` stops with `Cannot read properties of null (reading 'edgesOut')`. Update npm
with `sudo npm install -g npm`, or install Node from
[NodeSource](https://github.com/nodesource/distributions) or [nvm](https://github.com/nvm-sh/nvm),
then try again. Next.js apps and the standalone server install fine with npm 9.
:::

No project yet? Create one with `npx create-next-app@latest my-app --yes` or
`npm create nuxt@latest my-app`.

## Add Easy CMS

In your project directory:

```bash [pm]
npx 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/`, `backups/` 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 [pm]
npm 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 admin:create`.

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

## Read content

::: code-group

```ts [Nuxt: server/api/posts.get.ts]
export default defineEventHandler(async () => {
  const cms = await useEasyCMS()
  return cms.find('posts', { sort: '-createdAt', limit: 10 })
})
```

```tsx [Next.js: app/page.tsx]
import { connection } from 'next/server'
import { Suspense } from 'react'
import { getEasyCMS } from '@easy-cms/next'
import config from '@/easy-cms.config'

export default function Home() {
  return (
    <Suspense fallback={<p>Loading…</p>}>
      <Posts />
    </Suspense>
  )
}

async function Posts() {
  // Read on each request, so the page shows what was just published.
  await connection()
  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>
}
```

:::

**Nuxt:** if `server/` is a new folder, restart `npm run dev` so Nuxt picks it up.

**Next.js:** the page reads the CMS on each request: `await connection()` in a component inside
`<Suspense>`. This works whether or not `cacheComponents` is on in `next.config.ts` (new apps from
`create-next-app` have it on, and there `export const dynamic` is not allowed).

`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

- Model your content: [Configuration](./configuration) and [Fields](./fields)
- Decide who can do what: [Access control](./access-control)
- Before your first deploy: [Migrations & deployment](./deployment)
