Skip to content

Quickstart

Add Alinea to a Next.js project in a few steps. Prefer to let your coding agent do the work? Paste this prompt into Claude Code, Cursor or Codex and it follows the AI-assisted setup guide.

Read https://v2.alineacms.com/start.md and help me add Alinea to my Next.js project

Works with Claude Code, Cursor, Codex and more

1. Set up Next.js

Create a new Next.js project in a directory of your choice. To add Alinea to an existing project, skip to step 2. Alinea's Next.js integration uses the App Router and requires Node.js 24 or higher and React 19.

Terminal
npx create-next-app@latest

2. Install Alinea

Navigate to the newly created project directory and install the package with your preferred package manager.

npm install alinea@preview

3. Initialize the project

Alinea requires a config file which can be auto-generated by running alinea init. If you prefer plain JavaScript over TypeScript, rename the created file from cms.ts to cms.js.

npx alinea@preview init

In a Next.js project alinea init sets up everything the dashboard needs:

  • cms.ts (in src/ if your project has a src directory) holds your schema, workspaces and settings such as baseUrl, handlerUrl: '/api/cms' and adminPath: '/admin'. The production baseUrl is read from NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL, which Vercel sets during the build. Replace it with the URL of your site when you deploy elsewhere.

  • app/(alinea)/api/cms/route.ts is the API route the dashboard talks to. It passes your cms to createHandler from alinea/next. It imports @/cms, so check that the @/* path alias in tsconfig.json points to the folder that holds cms.ts.

  • content/pages/welcome.json is a first entry. content/media holds the entries of the media library, the uploaded files themselves are written to public/media (the workspace's mediaDir).

  • The dev and build scripts in package.json are rewritten to run through Alinea: next dev becomes alinea dev -- next dev and next build becomes alinea build -- next build.

  • /public/admin.html and /public/admin/ are added to .gitignore: alinea build writes the dashboard there.

4. Adjust Next.js config

Wrap your Next.js config with withAlinea. It serves the dashboard on your site at adminPath, forwards media file requests, and keeps the generated content package out of the server bundle:

import {withAlinea} from 'alinea/next'
import type {NextConfig} from 'next'

const nextConfig: NextConfig = {
  // Your Next.js options
}

export default withAlinea(nextConfig)

5. Start the dev server

Congratulations, Alinea is now ready to boot! Start your dev script:

npm run dev

The script runs alinea dev -- next dev: Alinea compiles cms.ts, starts its local server and then starts next dev with the settings withAlinea needs. Open the dashboard at http://localhost:3000/admin and have a look around.

6. Show content on a page

The generated Page type only has a title. Add an image and a body to it in cms.ts, and export it so your pages can query it:

cms.ts
import {Config, Field} from 'alinea'

// Export the type so your pages can query it
export const Page = Config.document('Page', {
  fields: {
    cover: Field.image('Cover image'),
    body: Field.richText('Body')
  }
})

Open the Welcome page in the dashboard, pick a cover image (upload one in the picker), write some text and publish. Then render it on your homepage by replacing app/page.tsx:

app/page.tsx
import {cms, Page} from '@/cms'
import {RichText} from 'alinea/ui'
import Image from 'next/image'
import {notFound} from 'next/navigation'

export default async function Home() {
  const page = await cms.first({type: Page, path: 'welcome'})
  if (!page) notFound()
  return (
    <main>
      <h1>{page.title}</h1>
      {page.cover && (
        <Image
          src={page.cover.src}
          width={page.cover.width}
          height={page.cover.height}
          alt={page.cover.alt ?? ''}
        />
      )}
      <RichText doc={page.body} />
    </main>
  )
}

cms.first finds the entry stored in content/pages/welcome.json by its path, and passing type: Page types the result. An image field returns the src, dimensions and alt text next/image needs, and withAlinea allows these urls in your image config. See the Image field for blur placeholders and focal points, and the Rich text field for rendering rich text with your own components.

7. Enable live previews

To see the page you are editing next to the editor, set preview: true in cms.ts (alinea init leaves it commented out) and render the preview component in your root layout:

app/layout.tsx
import {cms} from '@/cms'

export default function RootLayout({children}: {children: React.ReactNode}) {
  return (
    <html lang="en">
      <body>
        {children}
        <cms.previews widget />
      </body>
    </html>
  )
}

The widget prop adds a small toolbar to your pages to switch to the dashboard, read more in Live previews.

Next steps

  • Define your content types in the schema and pick fields.

  • Render content in your pages with queries.

  • Follow the tutorial to build a complete site.

  • Deploy your site with a backend for publishing.