What is Statix CMS?

Statix CMS is a free, lightweight content management system that uses GitHub as its content database, Cloudflare R2 for media storage, and Turso for user/auth data.

Unlike traditional CMS packages, Statix CMS is not installed as a dependency. When you run npx create-statix-cms, you get a complete, standalone Next.js application that you fully own and control.

Full ControlModify any file, component, or feature
No Vendor Lock-inThe code is yours, forever
No Breaking UpdatesYou decide when and what to update
Learn & CustomizeUnderstand exactly how everything works

How It Works

Statix CMS uses a unique architecture that separates concerns across three services.

GitHub

Content stored as JSON files. Every save creates a Git commit with full version history and metadata.

Cloudflare R2

S3-compatible media and image storage served via public URL. Organize into folders, track references.

Turso

Serverless SQLite for users, sessions, audit logs, and invitations. Edge-compatible session validation.

Authentication — Better Auth

  • Email OTP via Resend
  • GitHub OAuth (optional)
  • Google OAuth (optional)

Quick Start

1

Create a new project

Scaffold your CMS with a single command.

bash
npx create-statix-cms my-cms
cd my-cms
2

Set up required services

You need GitHub (content storage), Turso (user database), and Resend (email OTP). All have free tiers. Optional: Cloudflare R2, GitHub OAuth, Google OAuth.

3

Configure environment

Fill in .env with your credentials.

.env
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
GITHUB_OWNER=your-username
GITHUB_REPO=my-cms-content
BETTER_AUTH_SECRET=your-secret-here
BETTER_AUTH_URL=http://localhost:3000
TURSO_DATABASE_URL=libsql://your-db.turso.io
TURSO_AUTH_TOKEN=your-turso-token
RESEND_API_KEY=re_xxxxxxxxxxxx
RESEND_FROM_EMAIL=cms@yourdomain.com
4

Start the dev server

bash
npm run dev
# or
bun run dev
5

Create your admin account

Sign in with OTP, then promote yourself to Owner.

bash
# Add to .env:
INITIAL_ADMIN_EMAIL=your@email.com

# Then run:
npm run seed:admin
6

Configure your content

Edit src/statix.config.ts to define your collections and fields.

Prerequisites

1

GitHub Personal Access Token

Required

Go to github.com/settings/tokens, generate a classic token with repo scope. Create a separate repository for content.

2

Turso Database

Required

Sign up at turso.tech. Create a database and get the connection URL and auth token.

bash
turso db create my-cms
turso db show my-cms --url
turso db tokens create my-cms
3

Resend (Email OTP)

Required

Sign up at resend.com, create an API key, and verify your sender domain.

4

Cloudflare R2

Optional

Create a bucket, generate API token with Read & Write permissions, enable public access.

5

GitHub OAuth

Optional

Create OAuth App at github.com/settings/developers. Callback: {BETTER_AUTH_URL}/api/auth/callback/github

6

Google OAuth

Optional

Create OAuth 2.0 Client at Google Cloud Console. Redirect URI: {BETTER_AUTH_URL}/api/auth/callback/google

Environment Variables

All variables are validated at startup using Zod. The app will not start if required variables are missing.

VariableGITHUB_TOKEN
Requiredrequired
DescriptionGitHub Personal Access Token with repo scope
VariableGITHUB_OWNER
Requiredrequired
DescriptionGitHub username or organization
VariableGITHUB_REPO
Requiredrequired
DescriptionRepository name for content storage
VariableGITHUB_BRANCH
Requiredoptional
DescriptionBranch to use (default: main)
VariableBETTER_AUTH_SECRET
Requiredrequired
DescriptionAuth secret — generate with openssl rand -base64 32
VariableBETTER_AUTH_URL
Requiredrequired
DescriptionFull site URL (e.g., http://localhost:3000)
VariableTURSO_DATABASE_URL
Requiredrequired
DescriptionTurso connection URL (libsql://...turso.io)
VariableTURSO_AUTH_TOKEN
Requiredrequired
DescriptionTurso authentication token
VariableRESEND_API_KEY
Requiredrequired
DescriptionResend API key for sending OTP emails
VariableRESEND_FROM_EMAIL
Requiredrequired
DescriptionVerified sender email address
VariableGITHUB_CLIENT_ID
Requiredoptional
DescriptionGitHub OAuth app Client ID
VariableGITHUB_CLIENT_SECRET
Requiredoptional
DescriptionGitHub OAuth app Client Secret
VariableGOOGLE_CLIENT_ID
Requiredoptional
DescriptionGoogle OAuth Client ID
VariableGOOGLE_CLIENT_SECRET
Requiredoptional
DescriptionGoogle OAuth Client Secret
VariableR2_ACCOUNT_ID
Requiredoptional
DescriptionCloudflare account ID
VariableR2_ACCESS_KEY_ID
Requiredoptional
DescriptionR2 API token access key
VariableR2_SECRET_ACCESS_KEY
Requiredoptional
DescriptionR2 API token secret key
VariableR2_BUCKET_NAME
Requiredoptional
DescriptionR2 bucket name
VariableNEXT_PUBLIC_MEDIA_BASE_URL
Requiredoptional
DescriptionPublic URL for R2 bucket
VariableINITIAL_ADMIN_EMAIL
Requiredoptional
DescriptionEmail to promote to Owner via seed:admin

Configuration

All content modeling is done in src/statix.config.ts — the single source of truth for your CMS structure.

Base Configuration

statix.config.ts
import { StatixConfig } from "@/statix/types";

export const statixConfig: StatixConfig = {
  github: {
    owner: process.env.GITHUB_OWNER || "",
    repo: process.env.GITHUB_REPO || "",
    branch: process.env.GITHUB_BRANCH || "main",
  },
  mediaFolder: "uploads",
  i18n: {
    locales: ["en", "tr"],
    defaultLocale: "en",
  },
  roles: [],
  collections: [],
};

Singleton Example

Single pages with unique content (e.g., Home, About, Contact):

typescript
{
  slug: "home",
  label: "Home Page",
  type: "singleton",
  path: "content/home",
  icon: "Home",
  fields: [
    { name: "title", label: "Page Title", type: "text", required: true, localized: true },
    { name: "heroImage", label: "Hero Image", type: "image" },
    { name: "ctaText", label: "Button Text", type: "text" },
  ],
}

Collection Example

Repeatable items (e.g., Blog Posts, Team Members):

typescript
{
  slug: "blog",
  label: "Blog Posts",
  path: "content/blog",
  icon: "FileText",
  titleField: "title",
  fields: [
    { name: "title", label: "Title", type: "text", required: true, localized: true },
    { name: "date", label: "Publish Date", type: "date", required: true },
    { name: "featuredImage", label: "Featured Image", type: "image" },
    { name: "content", label: "Content", type: "blocks", localized: true },
  ],
}

i18n Configuration

typescript
i18n: {
  locales: ["en", "tr", "de", "fr"],
  defaultLocale: "en",
}

Content Modeling

12 field types available for building your content schema.

text

placeholder, localized

Single-line text input

textarea

rows, placeholder, localized

Multi-line text input

richtext

placeholder, localized

WYSIWYG rich text editor

image

Image picker from media library

file

File upload

number

Numeric input

select

options: [{label, value}]

Dropdown selection

date

Date picker

checkbox

Checkbox toggle

switch

Toggle switch

list

fields: Field[]

Repeatable group of fields

blocks

blocks: [{type, label, fields}]

Drag-and-drop content blocks

Roles & Permissions

Three system roles with granular permissions, plus support for custom roles.

RoleOwner
Manage Users
View Monitor
Manage Media
Manage Trash
ContentFull
RoleAdmin
Manage Users
View Monitor
Manage Media
Manage Trash
ContentFull
RoleEditor
Manage Users
View Monitor
Manage Media
Manage Trash
ContentView, Create, Edit

Custom Roles

Define custom roles in statix.config.ts with per-collection permissions: canView, canCreate, canEdit, canDelete, canPublish.

Admin Panel

Dashboard

/admin

Collection statistics, localization progress, recent activity feed.

Collections

/admin/[slug]

List view with search, pagination, and status indicators.

Content Editor

/admin/[slug]/[id]

Field-based form, block editor, locale switching, draft/publish.

Media Library

/admin/media

Upload, browse, organize, search, move, and delete media files.

Users

/admin/users

Create/invite users, assign roles, ban/unban, per-user audit logs.

Trash

/admin/trash

Browse soft-deleted content and media. Restore or permanently delete.

Monitor

/admin/monitor

Audit logs, activity charts, commit timeline, GitHub API rate limit.

API Reference

All API routes (except /api/auth and /api/media/serve) require authentication. Protected by CSRF validation and rate limiting (100 req/min per IP).

Authentication

Method*
Endpoint/api/auth/[...all]
DescriptionBetter Auth handler (login, logout, OTP, OAuth)

Content

MethodGET
Endpoint/api/content/[collectionSlug]/[id]
DescriptionGet content item
MethodPOST
Endpoint/api/content/[collectionSlug]/[id]
DescriptionCreate or update content
MethodDELETE
Endpoint/api/delete/[collectionSlug]/[id]
DescriptionSoft-delete content
MethodGET
Endpoint/api/collections/[slug]
DescriptionGet collection items list

Media

MethodPOST
Endpoint/api/upload
DescriptionUpload a file
MethodGET
Endpoint/api/media/list
DescriptionList media files
MethodPOST
Endpoint/api/media/delete
DescriptionSoft-delete media
MethodPOST
Endpoint/api/media/move
DescriptionMove media between folders
MethodGET
Endpoint/api/media/references
DescriptionFind content using a media file
MethodGET
Endpoint/api/media/stats
DescriptionStorage statistics
MethodGET
Endpoint/api/media/serve/[...path]
DescriptionServe media files (public)

Trash

MethodGET
Endpoint/api/trash/list
DescriptionList soft-deleted items
MethodPOST
Endpoint/api/trash/restore
DescriptionRestore item from trash
MethodDELETE
Endpoint/api/trash/delete
DescriptionPermanently delete item
MethodGET
Endpoint/api/trash/media/[filename]
DescriptionGet trashed media metadata

Admin

MethodGET
Endpoint/api/admin/users
DescriptionList users
MethodPOST
Endpoint/api/admin/users
DescriptionCreate or update user
MethodPOST
Endpoint/api/admin/users/[id]/avatar
DescriptionUpload user avatar
MethodGET
Endpoint/api/admin/activity
DescriptionActivity feed
MethodGET
Endpoint/api/admin/audit
DescriptionAudit logs

Tech Stack

CategoryFramework
TechnologyNext.js (App Router)
Version16.x
CategoryUI
TechnologyReact
Version19.x
CategoryStyling
TechnologyTailwind CSS
Version4.x
CategoryLanguage
TechnologyTypeScript
Version5.x
CategoryAuth
TechnologyBetter Auth
Version1.x
CategoryDatabase ORM
TechnologyDrizzle ORM
Version0.45.x
CategoryDatabase
TechnologyTurso (libsql)
Version
CategoryGitHub API
TechnologyOctokit
Version5.x
CategoryObject Storage
TechnologyAWS SDK S3 (Cloudflare R2)
Version3.x
CategoryState
TechnologyZustand
Version5.x
CategoryData Fetching
TechnologyTanStack React Query
Version5.x
CategoryForms
TechnologyReact Hook Form
Version7.x
CategoryValidation
TechnologyZod
Version4.x
CategoryRich Text
TechnologyProseKit
Version0.19.x
CategoryDrag & Drop
Technologydnd-kit
Version6.x
CategoryCharts
TechnologyRecharts
Version2.x
CategoryEmail
TechnologyResend
Version6.x
CategoryToasts
TechnologySonner
Version2.x
CategoryIcons
TechnologyTabler Icons React
Version3.x
CategoryComponents
Technologyshadcn/ui + Base UI
Version

Deployment

Deploy to Vercel (recommended) or any platform that supports Next.js.

1

Push your project to GitHub

2

Import the repository in Vercel Dashboard

3

Set all environment variables

4

Deploy

After Deployment

  • Update BETTER_AUTH_URL to your production domain
  • Update OAuth callback URLs to production domain
  • Generate a new BETTER_AUTH_SECRET for production

Other Platforms

Netlify
Railway
DigitalOcean App Platform
Self-hosted (npm run build && npm start)

Pre-launch Checklist

  • All required environment variables are set
  • BETTER_AUTH_SECRET generated fresh for production
  • BETTER_AUTH_URL set to production domain
  • OAuth callback URLs updated to production domain
  • Turso database created for production
  • R2 bucket configured with public access (if using media)
  • First admin user seeded via npm run seed:admin

Project Structure

Project Structure
src/
├── app/
│   ├── (statix)/
│   │   ├── admin/
│   │   │   ├── page.tsx              # Dashboard
│   │   │   ├── [collectionSlug]/     # Collection list + editor
│   │   │   ├── media/               # Media library
│   │   │   ├── users/               # User management
│   │   │   ├── trash/               # Trash management
│   │   │   └── monitor/             # Audit & monitoring
│   │   ├── auth/                    # Sign-in, invite acceptance
│   │   └── api/                     # API routes (20 endpoints)
│   ├── layout.tsx                   # Root layout
│   └── page.tsx                     # Public home page
│
├── statix/
│   ├── components/
│   │   ├── ui/                      # shadcn/ui base components
│   │   ├── editor/                  # Rich text & block editor
│   │   ├── fields/                  # Field type renderers
│   │   ├── collections/             # Collection list views
│   │   ├── dashboard/               # Dashboard widgets
│   │   ├── media/                   # Media library UI
│   │   ├── users/                   # User management UI
│   │   ├── trash/                   # Trash UI
│   │   ├── monitor/                 # Monitoring charts
│   │   ├── activity/                # Activity feed
│   │   ├── layout/                  # Navigation, breadcrumbs
│   │   ├── shared/                  # Reusable components
│   │   └── skeletons/               # Loading placeholders
│   ├── hooks/                       # 18 custom React hooks
│   ├── lib/                         # Core utilities
│   ├── stores/                      # Zustand state stores
│   ├── types/                       # TypeScript definitions
│   ├── db/                          # Drizzle ORM schema
│   └── content/                     # ui.json (translations)
│
├── statix.config.ts                 # Content model configuration
├── middleware.ts                    # CSRF, rate limiting, auth
└── lib/
    └── utils.ts                     # General utilities
app/(statix)/Route group for all CMS routes
statix/components/All React components, organized by feature
statix/hooks/18 custom hooks for data fetching, state, etc.
statix/lib/Core utilities (GitHub API, R2, auth, rate limiting)
statix.config.tsSingle source of truth for content modeling