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:
| Entry | When to use it |
|---|---|
@nexatron/chat | Headless NexatronClient for non-React apps, server code, or scripts. |
@nexatron/chat/react | React 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 variable | Default | What it controls |
|---|---|---|---|
border | --nx-border | 1px solid #e5e7eb | Outer container border + input/form separator |
radius | --nx-radius | 8px | Outer container border-radius |
bg | --nx-bg | #ffffff | Container background |
text | --nx-text | #0f172a | Primary text color |
muted | --nx-muted | #64748b | Hint / timestamp text |
accent | --nx-accent | #5B4FE5 | Send button + focused input outline |
userBg | --nx-user-bg | #EFEDFF | User message bubble background |
assistantBg | --nx-assistant-bg | #f8fafc | Assistant message bubble background |
errorText | --nx-error-text | #b91c1c | Error 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.