Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/alineacms/alinea/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through adding Alinea to a Next.js project from scratch. By the end you will have a working content schema, a running local dashboard, and a server component that queries typed content. All steps assume you have an existing Next.js 14+ project with the App Router.
cms.ts is the single source of truth for your entire content schema. It must be committed to your repository — Alinea derives all TypeScript types, dashboard forms, and database structure from it at build time.
1

Install Alinea

Add the alinea package to your project. Alinea is ESM-only, so make sure your package.json contains "type": "module".
npm install alinea
Alinea requires react and react-dom as peer dependencies. Both are already present in any Next.js project, so no additional installation is needed.
2

Initialize the config file

Run the init command to scaffold a starter cms.ts in your project root:
npx alinea init
This creates cms.ts with a minimal workspace and root configuration. Open the file and customize it in the next step.
3

Configure Next.js with withAlinea

Wrap your Next.js config with withAlinea so the framework can set up routing for the dashboard, configure image domains, and mark Alinea’s generated packages as server-external.
next.config.ts
import {withAlinea} from 'alinea/next'
import type {NextConfig} from 'next'

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

export default withAlinea(nextConfig)
withAlinea reads the generated @alinea/generated/settings.json to determine the dashboard path and automatically adds the necessary redirects and rewrites.
4

Create your cms.ts schema

Replace the scaffolded cms.ts (or write it from scratch) with a schema that matches your content model. Below is a complete example with a BlogPost document type containing a title and a body:
cms.ts
import {Config, Field} from 'alinea'
import {createCMS} from 'alinea/next'

// Define a Blog Post document type
const BlogPost = Config.document('Blog post', {
  fields: {
    title: Field.text('Title', {
      required: true
    }),
    body: Field.richText('Body text')
  }
})

// Define a Blog container that holds Blog Post entries
const Blog = Config.document('Blog', {
  contains: [BlogPost]
})

// Export types so they can be imported alongside the cms instance
export {BlogPost, Blog}

// Create and export the CMS instance
export const cms = createCMS({
  // URL where the API route handler is mounted (see next step)
  handlerUrl: '/api/cms',

  // All content types referenced in workspaces
  schema: {BlogPost, Blog},

  workspaces: {
    main: Config.workspace('Main', {
      // Directory where content files are stored
      source: 'content',
      roots: {
        blog: Config.root('Blog', {
          contains: [Blog]
        }),
        // Built-in media root for image and file uploads
        media: Config.media()
      }
    })
  }
})
createCMS (from alinea/next) returns a NextCMS instance that wires together the content database, preview support, and Next.js draft mode.
5

Create the API route handler

Alinea needs a single API route to handle dashboard requests, authentication, mutations, and preview tokens. Create the following file:
app/api/cms/route.ts
import {cms} from '@/cms'
import {createHandler} from 'alinea/next'

const handler = createHandler(cms)

export {handler as GET, handler as POST}
The route must match the handlerUrl you set in cms.ts (/api/cms in the example above). createHandler returns a standard Request → Response function compatible with the Next.js App Router route handler convention.
6

Start the dashboard

Start the Alinea development dashboard alongside your Next.js dev server:
npx alinea dev
The alinea dev command starts a local dashboard server and forwards requests to your Next.js app. Open http://localhost:3000/admin (or the adminPath you configured) to see the visual editor. Content changes are saved as files in your content/ directory in real time.
You can also pass --port to change the dashboard port, or --dir to point at a project in a subdirectory:
npx alinea dev --port 4000
Before deploying, run alinea build to generate types and the content cache used at build time:
npx alinea build
7

Query content in a server component

Import your cms instance and call cms.find(), cms.first(), or cms.get() directly inside any async Server Component or generateStaticParams function. Results are fully typed based on your schema. Remember to export any types you define in cms.ts so they can be imported in your components.
app/blog/page.tsx
import {cms, BlogPost} from '@/cms'

export default async function BlogPage() {
  // Fetch all published blog posts, selecting only the fields we need
  const posts = await cms.find({
    type: BlogPost,
    select: {
      title: BlogPost.title,
      path: BlogPost.path
    }
  })

  return (
    <ul>
      {posts.map(post => (
        <li key={post.path}>
          <a href={`/blog/${post.path}`}>{post.title}</a>
        </li>
      ))}
    </ul>
  )
}
app/blog/[slug]/page.tsx
import {cms, BlogPost} from '@/cms'

interface Props {
  params: {slug: string}
}

export default async function PostPage({params}: Props) {
  // Fetch a single entry by its path — first() returns null if not found
  const post = await cms.first({
    type: BlogPost,
    filter: {path: {is: params.slug}},
    select: {
      title: BlogPost.title,
      body: BlogPost.body
    }
  })

  if (!post) return <p>Not found</p>

  return (
    <article>
      <h1>{post.title}</h1>
      {/* Render rich text body */}
      <div>{post.body}</div>
    </article>
  )
}
Content is bundled with your application during the build, so both calls resolve from an in-memory database with zero network overhead during static generation.

What’s next?

With the basics in place you can explore the full field library (Field.select, Field.image, Field.date, Field.list, and more), add i18n locales to a root, configure roles and permissions, or set up a cloud backend for team collaboration. See the Configuration reference for all available options.

Build docs developers (and LLMs) love