React SDK

@nopii/react puts the browser SDK in context, so any component can protect the requests it sends and reveal the tokens it renders.

Preview

The SDKs are in preview and not yet published to npm or PyPI. Package names and APIs may change before 1.0.

Install

bash
npm install @nopii/browser @nopii/react

Vercel AI SDK chat

Wrap the app in NoPIIProvider with the routes to protect, then give useChat the protected fetch. Messages render in plaintext. /api/chat receives only tokens.

tsx
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { NoPIIProvider, useNoPIIFetch } from "@nopii/react";

export function App() {
  return (
    <NoPIIProvider
      config={{ baseUrl: "https://api.nopii.co", sessionToken: getSessionToken }}
      protect={[{ match: "/api/chat", request: "ai-sdk" }]}
    >
      <Chat />
    </NoPIIProvider>
  );
}

function Chat() {
  const fetch = useNoPIIFetch();
  const { messages, sendMessage } = useChat({
    transport: new DefaultChatTransport({ fetch }),
  });
  // render messages as usual
}

getSessionToken returns a client session from your backend, cached until shortly before it expires. See Initialize for an example, and the Node.js SDK for the route that mints it.

NoPIIProvider

PropMeaning
configOptions for NoPII.init. Or pass client with an instance you created.
protectRoutes to protect. Read once, when the provider mounts.
patchGlobalFetchAlso patch the global fetch while mounted. Off by default.
interceptRoute browser provider calls through NoPII (Tier 1). true or interception options.

Routes are registered before any child renders, so a component that sends a request as soon as it mounts is already protected. The global patch is applied and removed with the provider, and survives StrictMode's double mount.

Hooks and components

useNoPIIFetch and useNoPII

useNoPIIFetch() returns the protected fetch. useNoPII() returns the instance, for tokenize, detect, sockets, and everything else on the browser SDK.

Revealed and useRevealed

For tokenized text you render yourself, such as a history list loaded from your API:

tsx
import { Revealed, useRevealed } from "@nopii/react";

<Revealed text={message.body} fallback={<Skeleton />} />

const { text, pending, error } = useRevealed(message.body);

Tokens the page already holds render at once. Others are fetched under the reveal policy and replace the tokens when they arrive. A token the user may not see stays a token.

useRevealDom

For HTML you do not render as text, such as a third-party widget or server-rendered markup:

tsx
import { useRevealDom } from "@nopii/react";

function Transcript({ html }: { html: string }) {
  const ref = useRevealDom<HTMLDivElement>();
  return <div ref={ref} dangerouslySetInnerHTML={{ __html: html }} />;
}

It reveals tokens anywhere inside the element as they render and stops when the element unmounts. Subtrees marked data-nopii-raw are left alone. Prefer Revealed where you can, since React then owns the revealed text.

Related