Skip to main content

Embed Nexatron in a React app

This quickstart drops a fully working Nexatron chat surface into an existing React app in about 15 minutes: 5 minutes for the SDK install, 5 minutes for the embed-token endpoint, 5 minutes for the styling pass to match your brand.

We use <NexatronChat /> — the SDK's drop-in component — so you do not have to build a messages list, input, loading spinner, or error banner from scratch. If you want full control over the UI, drop down to the useNexatronChat hook (see Custom UI below).

1. Install the SDK (1 minute)

npm install @nexatron/chat
# or
pnpm add @nexatron/chat

The SDK supports React 18+. The package is split into three subpath exports:

EntryWhen to use it
@nexatron/chatHeadless NexatronClient for non-React apps, server code, or scripts.
@nexatron/chat/reactReact provider, hook, and <NexatronChat /> component.
@nexatron/chat/vanilla<nexatron-chat> Web Component for non-React contexts (Vue, Svelte, Astro, plain HTML).

2. Mint an embed token (5 minutes)

The browser never sees your Nexatron API key. Instead, your backend mints a short-lived embed token scoped to a tenant + a set of allowed connections, and your React app forwards that token to the SDK.

Add a tiny endpoint to your backend that proxies POST /api/v1/embed/tokens:

// pages/api/nexatron/token.ts (Next.js example)
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// 1) Authenticate the user with YOUR auth (session cookie, NextAuth, etc.)
const userId = await yourAuth(req);
if (!userId) return res.status(401).end();

// 2) Look up the Nexatron tenant + allowed connections for this user
const { tenantId, allowedConnections } = await yourTenantLookup(userId);

// 3) Mint the short-lived token
const r = await fetch("https://api.nexatron.io/api/v1/embed/tokens", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.NEXATRON_API_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
tenant_id: tenantId,
allowed_connections: allowedConnections,
ttl_seconds: 3600,
}),
});
if (!r.ok) return res.status(502).end();
const { token } = await r.json();
res.json({ token });
}

Why a short-lived embed token rather than a long-lived JWT in NEXT_PUBLIC_*: tokens minted server-side never reach the browser bundle, expire after the TTL, and can be revoked tenant-wide if a user offboards.

3. Wrap your tree in <NexatronProvider> (1 minute)

// app/providers.tsx
"use client";
import { NexatronProvider } from "@nexatron/chat/react";
import { useEffect, useState } from "react";

export function Providers({ children }: { children: React.ReactNode }) {
const [token, setToken] = useState<string | null>(null);

useEffect(() => {
fetch("/api/nexatron/token", { method: "POST" })
.then((r) => r.json())
.then((d) => setToken(d.token));
}, []);

if (!token) return <div>Loading Nexatron…</div>;

return (
<NexatronProvider
config={{
baseUrl: "https://api.nexatron.io",
token,
}}
>
{children}
</NexatronProvider>
);
}

4. Drop <NexatronChat /> into any route (1 minute)

// app/analytics/page.tsx
import { NexatronChat } from "@nexatron/chat/react";

export default function AnalyticsPage() {
return (
<main style={{ height: "70vh", maxWidth: 720, margin: "0 auto" }}>
<NexatronChat placeholder="Ask about your data…" />
</main>
);
}

That is the full integration. The component renders messages, handles loading + error states, manages the conversation ID, and auto-scrolls. Submit "show me revenue by region for Q4" and you should see the answer streamed back with the generated SQL and confidence score.

5. Theme it to match your brand (5 minutes)

<NexatronChat /> ships with a neutral default look that should fit most apps. Two ways to customize:

Option A — Inline theme prop

Best for one-off styling adjacent to the embed:

<NexatronChat
theme={{
accent: "#7c3aed",
bg: "#0f172a",
text: "#e2e8f0",
userBg: "#1e293b",
assistantBg: "#0b1220",
border: "1px solid #334155",
radius: "12px",
}}
/>

Option B — Global CSS

Best when the embed is consistent across the app. The component declares CSS custom properties on the .nx-chat class; override them anywhere in your stylesheet:

/* your app's global stylesheet */
.nx-chat {
--nx-accent: #7c3aed;
--nx-bg: #0f172a;
--nx-text: #e2e8f0;
--nx-user-bg: #1e293b;
--nx-assistant-bg: #0b1220;
--nx-border: 1px solid #334155;
--nx-radius: 12px;
}

The full theme surface

Token (prop)CSS variableDefaultWhat it controls
border--nx-border1px solid #e5e7ebOuter container border + input/form separator
radius--nx-radius8pxOuter container border-radius
bg--nx-bg#ffffffContainer background
text--nx-text#0f172aPrimary text color
muted--nx-muted#64748bHint / timestamp text
accent--nx-accent#5B4FE5Send button + focused input outline
userBg--nx-user-bg#EFEDFFUser message bubble background
assistantBg--nx-assistant-bg#f8fafcAssistant message bubble background
errorText--nx-error-text#b91c1cError banner text color

Custom UI

If <NexatronChat /> does not fit your design, drop down to the hook. The hook gives you state, dispatch, and lifecycle — you supply the layout:

import { useNexatronChat } from "@nexatron/chat/react";

function CustomChat() {
const { messages, sendMessage, isLoading, error } = useNexatronChat();
// …your own JSX
}

See the SDK API reference for the full hook signature.

Web Components (non-React)

Drop <nexatron-chat> into a plain HTML page:

<script type="module">
import { defineNexatronChat } from "@nexatron/chat/vanilla";
defineNexatronChat();
</script>

<nexatron-chat
api-url="https://api.nexatron.io"
api-key="<short-lived-embed-token>"
></nexatron-chat>

<style>
nexatron-chat { --nx-accent: #7c3aed; }
</style>

The Web Component uses the same --nx-* CSS variable surface, so a theme that works in one place works in the other.

What happens on the wire

User in your React app

▼ sendMessage("…")
NexatronClient (browser, with embed token)

▼ POST /api/v1/query
Nexatron API

▼ per-tenant pipeline (planner -> clarifier -> SQL -> validator)

▼ row-level-security filtered results
Browser

▼ ChatMessage appended to messages array
<NexatronChat /> re-renders

Every request carries the embed token, the SDK never holds your long-lived API key, and per-tenant RLS runs on the Nexatron side — so a token minted for tenant A can never read tenant B's data even if a browser forges the request.