npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bablic-io/sdk-vercel

v1.2.0

Published

Bablic server-side rendering for Next.js on Vercel.

Readme

@bablic-io/sdk-vercel

Server-side rendering for Bablic on Next.js and Vercel. Your translated pages are rendered on the server, so visitors and search engines receive finished, indexable HTML in their language. In the browser, Bablic keeps translating content that appears later.

Astro site? Use @bablic-io/sdk-astro instead, on Vercel too. This package is for Next.js only.

You need a Bablic account and project. New to Bablic? Sign up.

Requirements

  • A Bablic project with the Bablic script tag on every page of your site, in the HTML the server sends (not added by a tag manager or next/script with afterInteractive).
  • A Bablic Pro plan or a free trial. On other plans the SDK serves the original page unchanged (x-moxie: skipped, x-moxie-reason: please subscribe to pro tier) and Bablic keeps translating in the browser. After a plan change, allow about 15 minutes. Plans: Bablic dashboard.
  • Translated URLs set in your Bablic dashboard: language folders (for example example.com/fr-fr/…) or language subdomains (for example fr-fr.example.com). Cookie and query-parameter setups have no separate URL per language, so they keep translating in the browser only: the SDK serves their pages unchanged (x-moxie: skipped), even with a custom resolve.
  • The dashboard values must be exactly the URLs you want: a folder /fr-fr/ serves /fr-fr/…, never /fr/….
  • Next.js 16 or later with the App Router, on Vercel, on the Node.js runtime (not Edge).
  • Node.js 22.

Install

npm install @bablic-io/sdk-vercel

Set up

  1. Keep the Bablic script tag in your root layout.

    // app/layout.tsx
    <head>
      <script src="https://edge.bablic.com/moxie.min.js" data-oljs="P0000" async />
    </head>

    Copy the exact tag from your Bablic dashboard. P0000 stands for your project key.

    Add data-glweb-opacity="1" to the tag. Without it, the Bablic script hides each original-language page (near-zero opacity) until its settings arrive, which costs first paint and Lighthouse score. Translated pages served by the SDK never hide; the SDK sets this attribute on them. The tag can also load with defer. Keep the P at the start of data-oljs.

  2. Let the proxy see client-side navigations. Without this, Next.js strips the rsc header and _rsc parameter before the proxy runs.

    // next.config.ts
    const nextConfig = { skipProxyUrlNormalize: true };
    export default nextConfig;
  3. Add the proxy with your project key. The SDK reads your translated URLs from your Bablic dashboard settings, so you don't list languages or URLs in code.

    // proxy.ts
    import { withMoxie } from '@bablic-io/sdk-vercel';
    
    export const proxy = withMoxie(undefined, { project: 'P0000' });
    
    // Every page. Requests your project doesn't translate pass straight through.
    export const config = {
      matcher: ['/((?!_next/|api/|.*\\..*).*)'],
    };

    Already have a proxy? Pass it as the first argument: withMoxie(myProxy, { project: 'P0000' }). Requests the SDK doesn't translate go to it unchanged.

  4. Vercel Preview deployments with Deployment Protection: in your Vercel project, open Settings → Deployment Protection, turn on Protection Bypass for Automation, then redeploy. Without it, the SDK's fetch of each original page is sent to the Vercel login and no page is translated. The SDK sends the secret Vercel provides (VERCEL_AUTOMATION_BYPASS_SECRET) on its own.

  5. Preview domains: Bablic only answers for domains your project allows, and link rules are set per domain. Add the preview's domain as your project's staging domain in the dashboard. Use the branch's stable domain (<project>-git-<branch>-<team>.vercel.app) or a custom preview domain, since each deployment's own URL changes.

When you change your languages or URLs

The SDK reads your project settings from Bablic and caches them for 5 minutes; Bablic's own cache adds up to 10. A change in the dashboard reaches the SDK within about 15 minutes, with no code change or redeploy. To drop the SDK's copy sooner, POST {"config": true} to the revalidate route (Clearing the cache).

Check it works

Open one of your translated URLs twice, for example a page under a language folder:

curl -sI https://<your-translated-url>        # x-moxie: MISS, content-language: <language>
curl -sI https://<your-translated-url>        # x-moxie: HIT

On a protected Vercel Preview, add the bypass secret: curl -sI -H "x-vercel-protection-bypass: <secret>" https://<your-translated-url>.

View the page source: <html … data-moxie-mode="ssr">, one <script type="application/json" id="moxie-manifest">, and the text in the translated language.

While your project is in Staging, add ?bablic_preview=1 to the URL. Staging responses are never cached.

How it works

  1. A request arrives. The SDK checks it against your project's translated URLs (cached). Anything else passes straight through.
  2. A cached translation is served at once (x-moxie: HIT).
  3. On a miss, the SDK fetches the original page from the same deployment (for a language folder, the same path without the folder) and translates it on the server with your project's translations.
  4. It also translates the Next.js data React hydrates from, so the page stays translated after it loads, caches the page for pageTtl, and serves it (x-moxie: MISS). Client-side navigations are translated the same way.
  5. In the browser, the Bablic script translates content added later, without redoing the server's work.

Links and redirects follow your project's link rules in Bablic for the domain the request is on, the same rules Bablic applies in the browser. The SDK never invents a URL: with no rules, links stay as they are. The translated page's <html lang> is the language's code from your dashboard (unless your project sets its own HTML language), so browsers don't offer to translate it again. Translated URLs keep the original path under the language folder (/fr-fr/pricing); translated slugs aren't supported yet.

If translation fails or takes longer than timeoutMs, nothing partial is served or cached. Search engines get 503 with Retry-After: 60; visitors get the original page with X-Robots-Tag: noindex and Cache-Control: private, no-store. x-moxie: failed and x-moxie-reason say why.

Options

| Option | Default | Meaning | | --------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------- | | project | — | Your Bablic project key (data-oljs in the script tag). Required unless you pass resolve. | | pageTtl | 3600 | Seconds a translated page is fresh; then served stale and refreshed in the background. | | staleTtl | 604800 | Seconds an expired page may still be served while it refreshes. | | timeoutMs | 3000 | Translation budget per page while a visitor waits, in ms. | | backgroundTimeoutMs | 15000 | Budget for background refreshes and retries after a timeout, in ms. | | cache | Vercel Runtime Cache | Any store with get, set and expireTag. | | editorCookie | 'opemoxieoverride' | Requests with this cookie (the Bablic editor) get the original page, uncached. false turns this off. | | onResult | — | Called after each translation with { url, result, cache }, for logging. | | onUnsupported | — | Called once if your project uses cookie or query-parameter URLs, which can't be rendered on the server. | | resolve | your dashboard settings | Custom routing: map a request URL to { locale, sourceUrl }, or undefined to leave it. Overrides project. | | moxie | { mode: 'page' } | Run the Bablic script your page's tag loads. { mode: 'pinned', source, version } runs a copy you supply. | | host | https://edge.bablic.com/ | Bablic service URL. | | fetch | globalThis.fetch | fetch for the original page and Bablic's translation API. |

Response header x-moxie

| Value | Meaning | | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | HIT | Served from the cache. | | STALE | Served from the cache while a newer translation is prepared in the background. | | MISS | Translated for this request, then cached. | | redirect | The original page redirects; Location follows your link rules. x-moxie-reason shows both URLs. | | pass | The original page isn't a 200 HTML page (404, 410, JSON …); served as is, see x-moxie-reason. | | skipped | Not translated on the server: your plan doesn't include it, or the project doesn't translate this page (not live, no script tag, or translation off). | | failed | Translation failed; the original page was served under the failure rule. See x-moxie-reason. | | bypass | Request from the Bablic editor; original page, uncached. |

Requests that aren't translated URLs get no x-moxie header: they go to your app (or your own proxy) untouched.

SEO

What the SDK does on every translated page:

  • <html lang> and the content-language header carry the language's code from your dashboard, as a BCP-47 tag (fr-FR).
  • A canonical, og:url or twitter:url that names the original page is pointed at the translated URL, so each language is its own page, not a duplicate. A canonical that names a different page is left alone.
  • Titles, descriptions, Open Graph and Twitter tags, JSON-LD and image alt text are translated with the rest of the page.
  • JSON-LD (structured data) points at the translated page: url, item, mainEntityOfPage and @id values that name a page of your site get the translated URL, and inLanguage the page's language. Site-wide nodes, whose @id is your home page plus a # name (#organization, #website), stay the same on every page. Files and other sites' URLs are left alone.
  • hreflang values are valid BCP-47 tags. A language code with a private-use region, such as Spanish (Universal) ES-XM, is written es, in hreflang and in og:locale, since search engines ignore a region that doesn't exist.

What your project and site decide:

  • hreflang. Bablic adds alternates for every language plus x-default when hreflang is enabled for your project (ask Bablic support), and only on pages with no hreflang tags of their own. If your site prints its own hreflang tags, print one for every language, or none, and let Bablic add them. A partial set (only en and x-default) stops Bablic from adding the rest.
  • Sitemap. The SDK doesn't change your sitemap. Add each language's URLs (your original paths under each language folder) with your sitemap tool.
  • Images. og:image and other images stay as your site sets them, unless your project translates images.
  • Links. Links follow your project's link rules for the domain the page is served on. A domain with no rules keeps the original links, so set rules for your production domain and for any staging or preview domain.

Caching and speed

  • Fresh for pageTtl (1 hour): served from the cache (x-moxie: HIT).
  • Stale after that, or after a new deploy: still served at once (x-moxie: STALE), and refreshed in the background. After a deploy, a page whose original HTML is unchanged keeps its translation (one fetch of the original, no new translation). An expired page is always translated again, so edits in Bablic show up within pageTtl.
  • Kept for staleTtl (7 days) after it expires; only a page nobody visits that long is translated while a visitor waits.
  • Slow first translation: if a page's first translation runs past timeoutMs, the visitor gets the failure rule and the SDK finishes it in the background (backgroundTimeoutMs, 15 s), so the next visitor gets a HIT.
  • Warming: request each translated URL once after a deploy (without ?bablic_preview=1, which is never cached) to fill the cache before visitors arrive.
  • Server-Timing on every response shows where the time went, for example mx;desc="MISS", cache;dur=3, source;dur=42, translate;dur=870, total;dur=918. Browser dev tools and Lighthouse show it.

Hosting rules

  • Turn off ISR for your translated URLs. ISR ignores cookies, so it would cache Staging and editor responses.
  • A CDN in front of Vercel must not cache HTML for requests carrying the opemoxieoverride cookie.
  • Keep the Bablic script tag on every page, including the original-language pages.
  • Language subdomains must point at the same Vercel project as your main domain.

Clearing the cache

Pages refresh on their own after pageTtl. To clear them sooner, add a signed purge route:

// app/api/moxie/revalidate/route.ts
import { createRevalidateHandler, runtimeCache } from '@bablic-io/sdk-vercel';

export const POST = createRevalidateHandler({
  secret: process.env.MOXIE_REVALIDATE_SECRET!,
  cache: runtimeCache(),
});

POST {"tkey":"<translation key>"} (or add "url_hashes": [...] for single pages), or {"config": true} to re-read your project settings, with the header x-moxie-signature: sha256=<hex HMAC-SHA256 of the body, keyed with the secret>.

Links

License

MIT