Production-ready Next.js App Router integration layer for Ominity CMS.
@ominity/next is intentionally split into three concerns:
- CMS integration: stable models + API client normalization around
@ominity/api-typescript - Rendering engine: generic, recursive CMS component rendering with a project-owned component registry
- Next helpers: route resolution, static params, metadata, sitemap, and draft mode utilities
This package does not include project UI components. Each consuming website owns its own React components and visual design.
CMS-driven websites often need the same foundation repeatedly:
- fetch CMS pages/routes/menus/locales
- resolve translated slugs and locale-aware URLs
- render deeply nested CMS component trees
- keep pages server-first while allowing interactive client blocks
- support SSG, ISR, and SSR without rewriting integration logic per project
@ominity/next provides that foundation with explicit APIs and small, testable modules.
pnpm add @ominity/next @ominity/api-typescriptIf you use forms rendering, also install:
pnpm add react-hook-formPeer dependencies:
next^15 || ^16react^18 || ^19react-dom^18 || ^19
import { createCmsClient } from "@ominity/next/cms";
export const cmsClient = createCmsClient({
sdk: {
serverURL: process.env.OMINITY_API_URL ?? "",
security: {
apiKey: process.env.OMINITY_API_KEY ?? "",
},
language: "en",
},
debug: {
enabled: process.env.NODE_ENV !== "production",
},
});import { createCmsRegistry, defineCmsComponent } from "@ominity/next/rendering";
import { HeroBlock } from "@/components/cms/hero-block";
import { CarouselBlock } from "@/components/cms/carousel-block"; // can be a Client Component
export const cmsRegistry = createCmsRegistry([
defineCmsComponent("hero", HeroBlock),
defineCmsComponent("carousel", CarouselBlock),
]);import { createRoutingConfig } from "@ominity/next/cms";
import { fetchCmsPageForParams } from "@ominity/next/next";
import { renderCmsPage } from "@ominity/next/rendering";
import { cmsClient } from "@/lib/cms-client";
import { cmsRegistry } from "@/lib/cms-registry";
const routing = createRoutingConfig({
defaultLocale: "en",
locales: [
{ code: "en", language: "en", default: true },
{ code: "nl", language: "nl" },
],
localeSegmentStrategy: "language",
canonicalRedirectPolicy: "if-not-canonical",
});
export default async function CmsCatchAllPage({ params }: { params: { slug?: string[] } }) {
const routes = await cmsClient.getRoutes();
const resolved = await fetchCmsPageForParams({
client: cmsClient,
routes,
params,
routing,
});
if (!resolved) {
return null;
}
if (resolved.route.shouldRedirect) {
// optional: redirect(resolved.route.canonicalPath)
}
return renderCmsPage({
page: resolved.page,
registry: cmsRegistry,
context: {
page: resolved.page,
locale: resolved.route.locale,
path: resolved.route.incomingPath,
preview: false,
debug: false,
},
});
}This package does not force one rendering mode.
- Use
generateCmsStaticParamsfor SSG path generation. - Use Next route-level
revalidatefor ISR. - Use dynamic rendering when SSR is required.
Client Components can be nested inside rendered CMS pages without making the whole route client-rendered.
createCmsLinkResolver accepts route objects or string links.
Built-in route defaults:
page→/{locale?}/{slug}product→/{locale?}/p/{sku}-{slug}category→/{locale?}/c/{slug}(hierarchical category slugs supported)
Example route object:
{
resource: "route",
name: "page",
locale: "en",
parameters: {
id: 2,
slug: "contact-us"
}
}You can override link generation per route type with custom resolvers.
@ominity/next/forms provides the lounge-depot forms builder capabilities as a reusable package module:
FormRendererclient component for Ominity form definitionscreateOminityFormSubmitHandlerserver route helper- built-in themes (
tailwindDefaultTheme,unstyledTheme,loungeDepotFormTheme) createFormsClientwith response normalization + optional adapter integrationcreateShadcnFormComponentshelper for shadcn UI wiring
Example:
"use client";
import { FormRenderer, tailwindDefaultTheme } from "@ominity/next/forms";
export function ContactForm({ form }: { form: unknown }) {
return (
<FormRenderer
form={form}
styled
themeOverride={tailwindDefaultTheme}
defaultPhoneCountry="BE"
/>
);
}import { createOminityFormSubmitHandler } from "@ominity/next/forms";
const handler = createOminityFormSubmitHandler({
ominityApiKey: process.env.OMINITY_API_KEY ?? "",
ominityBaseUrl: process.env.OMINITY_API_URL,
});
export const POST = (request: Request) => handler(request);@ominity/next– full surface@ominity/next/cms– client, stable CMS types, routing, locales, metadata helpers@ominity/next/rendering– registry + recursive renderer@ominity/next/next– App Router integration helpers@ominity/next/forms– Ominity forms renderer + submit helpers
docs/architecture.mddocs/rendering.mddocs/component-registry.mddocs/routing.mddocs/i18n.mddocs/ssg-isr-ssr.mddocs/examples.mddocs/forms.mddocs/troubleshooting.md
pnpm typecheck
pnpm test
pnpm build