Installation
Create a new nextjs-cms project, connect it to MySQL, and sign in to the admin panel.
Requirements
- Node.js 20.9 or later (the minimum for Next.js 16).
- MySQL 8 or MariaDB, running locally or reachable over the network. You need a user that can create and alter tables.
- A package manager: pnpm (recommended), npm, yarn, or bun.
Quick start
pnpm create nextjs-cms@latest my-cmsnpx create-nextjs-cms@latest my-cmsyarn create nextjs-cms my-cmsbun create nextjs-cms my-cmsThe installer copies the project, installs dependencies, and runs the setup wizard (database credentials and admin password). Then create the tables for your sections and start the app:
cd my-cms
pnpm nextjs-cms --dev update-sections
pnpm devOpen http://localhost:3000 and sign in with the username Master and the password you chose during setup.
What the installer asks
◆ What is your project named? my-cms
◆ Add sample sections? ◻ Blog ◻ Category ◻ Settings
◆ What database provider? MySQL
◆ Initialize a Git repository? No| Prompt | What it does |
|---|---|
| Project name | Folder to create and the name in package.json. Pass it as the first argument to skip the prompt. Use . to install into the current (empty) folder. |
| Sample sections | Adds ready-made files to sections/: a Blog (hasItemsSection with title, cover photo, rich text, tags and a gallery), Category (categorySection), and Settings (simpleSection). Press enter with nothing selected to start empty. |
| Database provider | MySQL (including MariaDB). Other databases are not supported yet. |
| Git | Runs git init and stages the files. |
After copying the template, the installer:
- Installs dependencies with the package manager you ran it with.
- Generates random
ACCESS_TOKEN_SECRET,REFRESH_TOKEN_SECRET, andCSRF_TOKEN_SECRETvalues in.env. - Runs
nextjs-cms --dev setup, which asks for your database credentials, creates the CMS system tables, and asks for a password for the Master admin.
Your sections' own tables are created by update-sections, which is why you run it once before the first pnpm dev, and again every time you change a section.
The setup command
If setup failed (for example, MySQL was not running) or you skipped it, run it yourself from the project folder:
pnpm nextjs-cms --dev setupIt runs three steps in order:
- Database configuration: prompts for host, port, user, password and database name, tests the connection, and writes
DB_HOST,DB_PORT,DB_USER,DB_PASSWORD, andDB_NAMEto.env.development(or.env.productionwithout--dev). - System tables: creates the CMS's own tables (
admins,admin_privileges,access_tokens,editor_photos, logs, and bookkeeping tables) withdrizzle-kit push. - Master admin: asks for the password of the built-in
Masteraccount and grants it every permission.
If the system tables already exist, setup lists them and asks before dropping and recreating them. That deletes your admins and their permissions. Section tables (your content) are never touched by setup. To only reset the Master password, use set-master-admin instead.
If the database is already configured, skip step 1:
pnpm nextjs-cms --dev setup --continueThe --dev (-d) flag goes before the command name and tells the CLI to read .env.development. Without it, the CLI reads .env. See the CLI reference for every command.
Environment variables
The generated .env and .env.development files contain everything the admin needs. You only edit these by hand when deploying or when a feature needs a key.
# Auth: generated by create-nextjs-cms. Keep them secret and different per environment.
ACCESS_TOKEN_SECRET=...
REFRESH_TOKEN_SECRET=...
CSRF_TOKEN_SECRET=...
ACCESS_TOKEN_EXPIRATION=2h
REFRESH_TOKEN_EXPIRATION=1y
# Optional: needed by mapField (Google Maps)
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=
# Database: written to .env.development by `nextjs-cms -d setup` or `nextjs-cms -d db-config`
DB_HOST=localhost
DB_PORT=3306
DB_NAME=my_cms
DB_USER=root
DB_PASSWORD=...| Variable | Required | Description |
|---|---|---|
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD | Yes | MySQL connection. |
ACCESS_TOKEN_SECRET | Yes | Signs short-lived access tokens (JWT). |
REFRESH_TOKEN_SECRET | Yes | Signs long-lived refresh tokens. |
CSRF_TOKEN_SECRET | Yes | Signs the CSRF token checked on every mutating request. |
ACCESS_TOKEN_EXPIRATION | Yes | Access token lifetime, in jsonwebtoken format (2h, 30m). |
REFRESH_TOKEN_EXPIRATION | Yes | Refresh token lifetime (1y, 30d). |
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY | For mapField | Google Maps JavaScript API key. |