@shipseo/next
v0.1.1
Published
Next.js SDK for Shipseo — SEO metadata + Core Web Vitals for AI-built sites
Maintainers
Readme
@shipseo/next
Next.js SDK for Shipseo — resolves per-route SEO metadata from the Shipseo dashboard at render time, and beams Core Web Vitals from real users back to Shipseo.
Zero-dep. TypeScript-first. Cannot crash your page (always falls back to what you passed).
Install
npm i @shipseo/next
# or
pnpm add @shipseo/nextPeer dep: Next.js 14 / 15 / 16.
Quick start
1. Metadata (per-route, server-side)
// app/layout.tsx (or any page/layout with generateMetadata)
import { shipseoMetadata } from '@shipseo/next';
export async function generateMetadata() {
return shipseoMetadata({
route: '/',
fallback: {
title: 'My AI-built app',
description: 'Rendered from the fallback until Shipseo is wired.',
},
});
}Set your site key in the env to activate live-config lookup. Without it, the SDK returns fallback verbatim — safe to install today, works even before you create an account.
# .env.local — one variable, and it covers both halves of the SDK
NEXT_PUBLIC_SHIPSEO_SITE_KEY=ss_pk_your_keyUse the NEXT_PUBLIC_ name. shipseoMetadata() runs on the server and can read either, but reportWebVital() runs in the browser, and Next only inlines variables with that prefix into the client bundle. SHIPSEO_SITE_KEY alone still works for metadata — and reports no vitals at all, silently.
If your key must stay off the client for some reason, set SHIPSEO_SITE_KEY for the server and pass it to the browser yourself with configure().
2. Web Vitals (from real users)
// app/vitals.tsx
'use client';
import { useReportWebVitals } from 'next/web-vitals';
import { reportWebVital } from '@shipseo/next/vitals';
export function WebVitals() {
useReportWebVitals(reportWebVital);
return null;
}This component runs in the browser, so it only reports when the key reached the client — that is what NEXT_PUBLIC_SHIPSEO_SITE_KEY is for. Without a key reportWebVital() returns silently and nothing arrives in your dashboard.
// app/layout.tsx — mount once
import { WebVitals } from './vitals';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<WebVitals />
</body>
</html>
);
}API
shipseoMetadata(opts): Promise<Metadata>
| Option | Type | Notes |
|---|---|---|
| route | string | Path used as lookup key. Include leading slash. |
| fallback | Metadata | Required. Rendered when Shipseo is unreachable or in "install mode". |
| siteKey? | string | Per-call override for the effective site key. |
Guarantees:
- Never throws. On any error (timeout, 5xx, malformed JSON), returns
fallback. - Never blocks longer than
timeoutMs(default 3s). - Merges Shipseo response over
fallbackper-field. Fields absent from the response are inherited fromfallback.
reportWebVital(metric)
| Option | Type | Notes |
|---|---|---|
| metric | WebVitalMetric | Whatever next/web-vitals or the web-vitals npm package emits. |
Guarantees:
- Server-side no-op (safe to import anywhere).
- Silent no-op when no
siteKeyis configured. - Uses
navigator.sendBeaconwhen available; falls back to keepalivefetch. - Never throws.
configure(cfg)
import { configure } from '@shipseo/next';
configure({
siteKey: 'ss_prod_xxx',
apiUrl: 'https://shipseo.dev', // override for self-hosted
ttlMs: 5 * 60 * 1000, // metadata cache TTL
timeoutMs: 3000, // per-request timeout
onError: (err, ctx) => Sentry.captureException(err, { extra: ctx }),
});Alternatively, set env vars: SHIPSEO_SITE_KEY, SHIPSEO_API_URL.
FAQ
What if Shipseo is down?
Your page renders with fallback. Every shipseoMetadata() call is bounded by timeoutMs (3s default). No crash, no empty <head>.
Do I get vendor lock-in?
No. The SDK is one npm package + a SHIPSEO_SITE_KEY env var. Delete both and you're back to hardcoded metadata — no schema migration, no data extraction. Every fallback you write is your safety net.
Bundle size?
The server export is <2KB. Vitals is client-only via @shipseo/next/vitals — mount it only where you need CWV reporting.
Where's the source? github.com/shipseo/next
License
MIT
