@soloworks/smking-next
v0.22.2
Published
AI-native SEO (AEO) for Next.js — auto-inject JSON-LD, FAQ, AI summary, and SEO metadata so AI crawlers (ChatGPT, Perplexity, Google AI) can cite your pages.
Maintainers
Readme
@soloworks/smking-next
AI-native SEO (AEO) for Next.js. One server component injects JSON-LD, OG tags, AI summary, and FAQ on every page so AI crawlers (ChatGPT, Perplexity, Google AI) can cite your content.
- One server component in your root layout — every URL gets its own AEO content automatically (
/products/nike-air,/products/adidas/red, anything dynamic, no codemod needed). - Fail-fast, fail-open. 2-second timeout + Next.js ISR — if Page Zero is down, your page renders without injection. Never blocks.
- Push updates — webhook handler invalidates only the changed paths via
revalidateTag.
Install
Your Page Zero dashboard generates the source-of-truth install prompt with the real environment values, selected surfaces, Blog path, route shims, and version-aligned verification command.
Two ways to get it:
# Option 1 — one-shot wizard (installs deps + writes env + runs doctor)
npx @soloworks/smking-wizard@latest
# Option 2 — copy the prompt manually from your Page Zero dashboard's
# install panel into your editor / coding agent.The wizard can install AEO + SEO, Blog only, or both. It owns package installation, selected layout mounts, environment writes, Blog routes, the surface-aware doctor, and the real production build.
Authoritative metadata
Use withSmkingMetadata() from the real root layout's generateMetadata
export, then mount <SmkingAEO includeSeo={false} /> for JSON-LD and hidden
AEO fragments. This produces one authoritative title and description instead
of relying on render order.
export async function generateMetadata(): Promise<Metadata> {
return withSmkingMetadata(hostMetadata, {
apiKey: process.env.SMKING_API_KEY!,
});
}
<SmkingAEO
apiKey={process.env.SMKING_API_KEY!}
includeSeo={false}
/>How outage tolerance works
getAeoContent wraps fetch with AbortSignal.timeout(2000) and Next.js ISR (next: { revalidate: 3600, tags: ['smking:path:<path>'] }):
- Cache hit (the common path): zero network. Tags allow webhook-driven invalidation.
- Cache miss + Page Zero healthy: one network roundtrip, response cached for 1h.
- Cache miss + Page Zero down / hung: returns null after at most 2s, page renders without injection. Next.js ISR retries on the next request after
revalidate. - 5xx / 4xx / parse error: same fail-open path.
No circuit breaker, no retry, no status command — Next.js infrastructure already covers what those would do.
API
<SmkingAEO /> props
interface SmkingAEOProps {
apiKey: string; // required
baseUrl?: string; // override SMKING_BASE_URL env
path?: string; // explicit path; auto-resolved from headers() otherwise
url?: string; // explicit absolute request URL when path is overridden
revalidate?: number; // ISR seconds; default 3600 (1h)
includeSeo?: boolean; // false when using withSmkingMetadata()
}Path auto-detection works for any URL schema (/products/[slug], /shop/[cat]/[id]/[variant]). Pass path explicitly only for static-export contexts where headers() is unavailable.
getAeoContent(params)
Lower-level helper if you want to fetch the AEO response and render yourself. Same params, returns Promise<AeoResponse | null>.
import { getAeoContent } from '@soloworks/smking-next';
const aeo = await getAeoContent({ apiKey: ..., path: '/products/abc' });
if (aeo?.status === 'ready') {
// aeo.jsonLd, aeo.faq, aeo.summary, aeo.seo, aeo.chatLinks, ...
}Publish webhook
The source-of-truth install prompt creates
app/api/smking/webhook/route.ts, which re-exports POST from
@soloworks/smking-next/webhook. The handler verifies the
SMKING_WEBHOOK_SECRET HMAC and invalidates the affected AEO or Blog cache
tags.
Mount the runtime once (v0.16.1+)
Add <SmkingRuntime /> once in your root layout — it emits a <link> to the saas-served CSS and a <script async> to the bundled Web Component runtime IIFE. Browser caches both per saas-controlled stale-while-revalidate headers, so the cost amortises across every <SmkingCms> instance on the page.
// app/layout.tsx
import { SmkingRuntime } from "@soloworks/smking-next";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
<SmkingRuntime apiKey={process.env.SMKING_API_KEY!} />
{children}
</body>
</html>
);
}Optional baseUrl prop overrides process.env.SMKING_BASE_URL. Default: https://getpagezero.com.
Without <SmkingRuntime />, <SmkingCms> content still renders but the Tailwind utility classes from the dashboard's cva variants resolve to dead strings — the page reaches the browser unstyled. The wizard installer auto-adds this mount; if you're upgrading manually from < v0.16.1, add the one line above.
Page Zero Blog (optional)
If you publish Blog pages from the Page Zero dashboard, render them with the
<SmkingCms slug="…" /> Server Component.
Choose a URL path such as /blog, /knowledge, or /shop/articles and set
the same value as SMKING_CMS_PATH. The wizard writes it automatically.
Root AEO requests skip that path so <SmkingCms> is the only source of SEO and
JSON-LD on Blog pages.
Optional catch-all route (handles the CMS root + nested slugs)
Page Zero CMS slugs can be nested, and the CMS root uses an empty slug. Use
Next.js optional catch-all [[...slug]] so one route handles /blog, flat
slugs, and every nested depth:
// app/blog/[[...slug]]/page.tsx
import { SmkingCms } from "@soloworks/smking-next/cms";
export default async function Page({
params,
}: {
params: Promise<{ slug?: string[] }>;
}) {
const { slug } = await params;
return (
<SmkingCms
apiKey={process.env.SMKING_API_KEY!}
slug={slug?.join("/") ?? ""}
/>
);
}At /blog, params.slug is undefined and maps to the empty CMS slug. Flat
and nested paths map to "hello" and "seo/intro" respectively. A required
[...slug] would miss the CMS root.
Markup contract for CSS
<article class="smk-cms" data-smking="cms">
<h1 class="smk-cms__title">…</h1>
<p>standard prose</p>
<h2>headings</h2>
<ul>
<li>lists</li>
</ul>
<blockquote>…</blockquote>
<pre><code>code blocks</code></pre>
<a href="…">links</a>
<img src="…" alt="…" />
<div
data-type="gallery"
data-layout="grid"
data-columns="3"
class="smk-gallery smk-gallery--grid"
>
<figure class="smk-gallery__item">
<img src="…" alt="…" loading="lazy" />
<figcaption>optional</figcaption>
</figure>
</div>
</article>Standard nodes inherit your site's prose styling. Gallery is the only opinionated structure — provide CSS for .smk-gallery (typically a grid with grid-template-columns: repeat(var(--smk-gallery-cols, 3), 1fr) since the SDK injects --smk-gallery-cols inline).
Cache invalidation
CMS responses cache for 5 minutes by default via Next.js data cache (tags: ["smking:cms_page:<slug>"]). When you publish, rename, or archive a page in the dashboard, smking SaaS POSTs a signed webhook to https://<your-site>/api/smking/webhook (mount with one-line re-export — see Install). The handler verifies HMAC against SMKING_WEBHOOK_SECRET and calls revalidateTag so the next visitor reads fresh content.
If SMKING_WEBHOOK_SECRET is unset, the webhook route returns 503 and cache invalidation falls back to TTL-based expiry.
Versions
See CHANGELOG.md. Aligned with smking/laravel for SaaS-side parity.
License
MIT
