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

@notlimey/optimizely-nextjs-multisite

v0.0.3

Published

A next.js package to support optimizely multisite

Readme

@notlimey/optimizely-nextjs-multisite

A Next.js package designed to seamlessly integrate Optimizely CMS Multisite routing and context into your Next.js App Router applications.

This package securely resolves the correct site definition based on incoming requests in Next.js Middleware, signs the context, and securely passes it down to your Next.js server components via request headers.

Features

  • Next.js App Router Native: Uses next/server and next/headers out-of-the-box.
  • Middleware Integration: Intercepts requests, matches the incoming host against your Optimizely site definitions, and securely forwards the context.
  • Secure by Default: Uses a signature secret to prevent header spoofing from malicious clients.
  • Strongly Typed: Full TypeScript support for your site configuration and resolved context.

Requirements

  • Node.js runtime (Edge runtime is NOT supported as the package relies on Node.js Crypto for signatures)
  • Next.js 16+
  • App Router (app directory)

Installation

npm install @notlimey/optimizely-nextjs-multisite
# or
yarn add @notlimey/optimizely-nextjs-multisite
# or
pnpm add @notlimey/optimizely-nextjs-multisite

Setup & Usage

1. Create your Configuration

First, create a centralized file to initialize your site configuration. This configures how your app retrieves your SiteDefinitions (usually via Optimizely Content Graph GraphQL API) and defines your security signature secret.

The graphql example used in sdk.getSiteDefinitions can be found here: getSiteDefinitions.graphql

// src/multisite.ts (or wherever you keep your utilities)
import { createSiteConfiguration } from "@notlimey/optimizely-nextjs-multisite";
// Import your generated GraphQL SDK or fetcher here
import sdk from "./your-sdk";

export const siteConfiguration = createSiteConfiguration({
  security: {
    // Essential: Keep this secret in your environment variables!
    headerSignatureSecret: process.env.OPTIMIZELY_MULTISITE_HEADER_SECRET || "",
  },
  // The get method is called internally to fetch definitions
  // I recommend adding caching here if your definitions don't change often.
  get: async () => {
    const res = await sdk.SiteStructure();
    return res.SiteDefinition?.items || [];
  },
});

2. Add to Middleware

In your Next.js Middleware, use handleProxy to process incoming requests. This will match the request host, determine the proper site and language, and attach securely signed HTTP headers forwarding this context down.

// src/middleware.ts
import { NextResponse, type NextRequest } from "next/server";
import { siteConfiguration } from "./multisite";

export async function middleware(request: NextRequest) {
  try {
    // Automatically maps the request to an Optimizely site and proxies
    // the modified request (with secure headers) to Next.js
    return await siteConfiguration.handleProxy(request);
  } catch (error) {
    console.error("Multisite resolution failed", error);
    // Fallback or error handling
    return NextResponse.next();
  }
}

export const config = {
  // Avoid running middleware on static files and Next.js internals
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};

3. Consume in Server Components

Inside your layouts, pages, or any Server Component, simply call .details() to extract the parsed multisite context securely.

// app/[[...slugs]]/page.tsx
import { siteConfiguration } from "../../src/multisite";
import sdk from "../../src/your-sdk";

export default async function Page() {
  // details() reads next/headers and validates the secure signature
  const { siteId, relativePath, language, masterLanguage, currentHost } =
    await siteConfiguration.details();

  // Use the context to fetch the correct Optimizely content
  const page = await sdk.getContentByPath({
    siteId,
    relativePath,
    language,
  });

  return (
    <main>
      <h1>{page?.name}</h1>
      {/* Render your page content */}
    </main>
  );
}

Advanced Example: Server Action / Helper

You can also use .details() directly in your shared data-fetching utilities:

// src/helpers/getChildren.ts
import { siteConfiguration } from "../multisite";
import sdk from "../your-sdk";

export const getChildren = async (parentGuid: string) => {
  const { siteId, language } = await siteConfiguration.details();

  const content = await sdk.GetContentByParent({
    parentId: parentGuid,
    siteId,
    language,
  });

  return content?.Content?.items || [];
};

How Hostname Matching Works

When handleProxy is called in Middleware, the package resolves the correct site definition by comparing the incoming request's host against the hosts defined in your Optimizely site structure.

The matching process follows a specific priority:

  1. Exact Match: It looks for a host definition that exactly matches the request host (including port), ignoring case.
  2. Wildcard Port Match: If no exact match is found, it checks for definitions containing a wildcard port (e.g., example.com:*). This matches if the hostnames match, regardless of the port.
  3. Catch-all Match: A host definition of exactly * acts as a catch-all and will match any incoming request.
  4. Fallback: If multiple matches are found but no exact or wildcard port match takes precedence, it falls back to the first available match.

If no matching host is found, handleProxy will throw an error.

Security considerations

The headerSignatureSecret is critical. It ensures that the x-opti-multisite-* headers passed down from Middleware haven't been spoofed by an external request. Make sure process.env.OPTIMIZELY_MULTISITE_HEADER_SECRET is a long, random string, and is securely stored in your .env.local or hosting provider's secrets manager.

Note

Documentation is partially written with google gemini, can include incorrect information.

License

ISC