Skip to content
Z-CMS is being built in the open on GitHub. Join the community β†’

← All posts

Designing a Modern Theme Engine for Next.js

Jul 13, 2026 Β· Z-SOFT Admin Β· 4 min read

Separating presentation from the CMS Core in a React and Next.js architecture

Why a Next.js theme engine is different

popular CMS demonstrated the value of changing a site’s appearance without changing the CMS Core. In Next.js, however, a theme is not only HTML and CSS. It includes React components, server and client boundaries, routing, data contracts, bundling, static generation and cache behavior.

Theme responsibilities

A Z-CMS theme controls layout, typography, colors, navigation, content templates, blocks, archives, search and responsive presentation. Business logic, authorization and direct database access remain in Core services or plugins.

Theme package structure

z-theme-orchid/
β”œβ”€β”€ theme.json
β”œβ”€β”€ package.json
β”œβ”€β”€ public/preview.png
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ layouts/
β”‚   β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ blocks/
β”‚   β”œβ”€β”€ styles/
β”‚   └── index.ts
└── dist/
{
  "name": "@z-cms/theme-orchid",
  "version": "1.0.0",
  "engine": { "z-cms": ">=1.0.0" },
  "templates": {
    "home": "HomePage",
    "post": "PostPage",
    "page": "Page",
    "archive": "ArchivePage",
    "404": "NotFoundPage"
  }
}

Stable view-model contracts

Themes do not query database tables. Core services transform domain entities into stable view models, allowing the database schema to evolve without breaking every theme.

export interface PostPageModel {
  site: SiteViewModel;
  post: PostViewModel;
  navigation: NavigationViewModel[];
  widgets: WidgetAreaViewModel[];
  seo: SeoViewModel;
}
export default function PostPage({ post, site, navigation }: PostPageModel) {
  return (
    <DefaultLayout site={site} navigation={navigation}>
      <article>
        <h1>{post.title}</h1>
        <div>{post.content}</div>
      </article>
    </DefaultLayout>
  );
}

Template resolution

Custom template selected by editor
   ↓
Content-type template
   ↓
Generic page template
   ↓
Core fallback template

A missing theme template does not take the site offline because the Core can fall back to a compatible default.

Blocks as the content composition layer

[
  { "type": "hero", "props": { "title": "Build faster with Z-CMS" } },
  { "type": "feature-grid", "props": { "columns": 3 } }
]

Themes register visual renderers for known blocks, while plugins can introduce new block types. If a theme does not override a plugin block, the plugin’s default renderer or a Core fallback is used.

Design tokens and customization

{
  "tokens": {
    "colors": {
      "primary": { "type": "color", "default": "#4f46e5" },
      "background": { "type": "color", "default": "#ffffff" }
    },
    "layout": {
      "containerWidth": { "type": "size", "default": "1200px" }
    }
  }
}
:root {
  --z-color-primary: #4f46e5;
  --z-color-background: #ffffff;
  --z-container-width: 1200px;
}

The administrator can customize approved tokens without editing source code or forking the theme.

Server Components and Client Components

Most templates should remain Server Components for SEO, performance and lower browser JavaScript. Interactive pieces such as mobile menus or carousels are explicit Client Components. The theme build pipeline validates the boundary and bundles both correctly.

Theme security

  • Build and validate a theme before activation.
  • Allow only approved dependencies.
  • Block imports from Node.js internals and Core private modules.
  • Expose a public @z-cms/theme-sdk instead of database access.
  • Scan marketplace source and bundles for forbidden capabilities.

Inheritance, preview and activation

A child theme can override selected templates and styles while extending a parent theme. Administrators preview a candidate theme through an authorized preview context, while public visitors continue seeing the active theme.

Validate theme
   ↓
Build or load verified bundle
   ↓
Run render smoke test
   ↓
Update active-theme pointer
   ↓
Invalidate scoped caches

Multi-tenant cache keys

tenantId + themeId + themeVersion + route + locale + contentVersion

Including the theme version in the cache key prevents a page rendered by an old theme from being served after activation.

Conclusion

A Next.js theme engine requires more than replaceable CSS. It needs package contracts, view models, template resolution, blocks, design tokens, server/client boundaries, preview, safe activation and tenant-aware caching. Z-CMS aims to preserve the flexibility of classic CMS themes while giving developers a modern React and TypeScript workflow.

GitHub: https://github.com/zscontributor/z-cms
Website: https://z-cms.org