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.
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.
npx create-next-app@latest2. Install Alinea
Navigate to the newly created project directory and install the package with your preferred package manager.
npm install alinea@preview3. 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 initIn a Next.js project alinea init sets up everything the dashboard needs:
cms.ts(insrc/if your project has asrcdirectory) holds your schema, workspaces and settings such asbaseUrl,handlerUrl: '/api/cms'andadminPath: '/admin'. The productionbaseUrlis read fromNEXT_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.tsis the API route the dashboard talks to. It passes yourcmstocreateHandlerfromalinea/next. It imports@/cms, so check that the@/*path alias intsconfig.jsonpoints to the folder that holdscms.ts.content/pages/welcome.jsonis a first entry.content/mediaholds the entries of the media library, the uploaded files themselves are written topublic/media(the workspace'smediaDir).The
devandbuildscripts inpackage.jsonare rewritten to run through Alinea:next devbecomesalinea dev -- next devandnext buildbecomesalinea build -- next build./public/admin.htmland/public/admin/are added to.gitignore:alinea buildwrites 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 devThe 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:
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:
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:
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.