i99dash docs
Getting startedFrameworks

Next.js

App router + static export. The two pitfalls to know up front, plus a working pattern.

Use Next.js when you want file-based routing, SSG, React server components, or any ecosystem integration. Two pitfalls to know up front:

  1. The host bridge lives on window. It does not exist during SSR. Any code that reads it must run in a Client Component inside useEffect / componentDidMount.
  2. For publishing, you want a fully static output (output: 'export'). The host doesn't run a Next.js server — your mini-app is shipped as bytes to the CDN.

Install

pnpm add @i99dash/sdk @i99dash/sdk-react
pnpm add -D @i99dash/sdk-cli

@i99dash/sdk-react is optional but pulls its weight: one provider

  • one hook per family, with a fallback that handles SSR / no-host out of the box. The plain SDK example below also works.

Static export

next.config.mjs:

export default {
  output: 'export',
  trailingSlash: true,
  images: { unoptimized: true },
};

sdk.config.json

{
  "appRoot": "./out",
  "mocksDir": "./mocks",
  "buildCommand": "next build"
}

next build with output: 'export' drops the static site in ./out; the SDK copies that to dist/ and tarballs it.

Client-component pattern

Mount the provider once at the top of your client tree; every hook below picks it up.

app/Providers.client.tsx:

'use client';
import { useEffect, useState } from 'react';
import { MiniAppProvider } from '@i99dash/sdk-react';
import { createClientOrSSR, type MiniAppClient } from '@i99dash/sdk';

export function Providers({ children }: { children: React.ReactNode }) {
  // SSR-safe: returns null on the server, real client on the browser.
  const [client, setClient] = useState<MiniAppClient | null>(() => createClientOrSSR());
  useEffect(() => setClient(createClientOrSSR()), []);
  return <MiniAppProvider client={client}>{children}</MiniAppProvider>;
}

app/layout.tsx:

import { Providers } from './Providers.client';
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

app/fuel/Fuel.client.tsx:

'use client';
import { useMiniAppContext } from '@i99dash/sdk-react';

export default function Fuel() {
  const { data: ctx, loading } = useMiniAppContext({
    fallback: { locale: 'en', isDark: false } as any,
  });
  if (loading) return <p>loading…</p>;
  return <pre>{JSON.stringify(ctx, null, 2)}</pre>;
}

Without sdk-react (plain SDK)

app/fuel/Fuel.client.tsx:

'use client';
import { useEffect, useState } from 'react';
import { MiniAppClient, type MiniAppContext } from '@i99dash/sdk';

export default function Fuel() {
  const [ctx, setCtx] = useState<MiniAppContext | null>(null);

  useEffect(() => {
    const client = MiniAppClient.fromWindow();
    client
      .getContext()
      .then(setCtx)
      .catch((e) => console.error(e));
  }, []);

  if (!ctx) return <p>loading…</p>;
  return <pre>{JSON.stringify(ctx, null, 2)}</pre>;
}

app/fuel/page.tsx:

import Fuel from './Fuel.client';
export default function Page() {
  return <Fuel />;
}

Running both dev servers

# terminal 1 — Next.js
pnpm next dev --port 3000

# terminal 2 — sdk-dev-server pointing at a static build
pnpm next build && pnpm next export
sdk-i99dash dev --port 5173

Or inline the shim during dev: add <script src="http://127.0.0.1:5173/_sdk/bridge.js"> to your root layout behind a process.env.NODE_ENV === 'development' check.

Working example

A complete Next.js 15 app-router project — including driving-state banner, RTL flip on Arabic context, and callApi against a local fixture — lives at examples/nextjs-example/ in the SDK repo. Clone, pnpm install, pnpm dev.

Common gotchas

SymptomFix
window is not defined build errorYou called MiniAppClient.fromWindow() outside useEffect. Wrap it.
next build errors with "page is not exportable"A Server Component imports the SDK runtime. Move the import into a 'use client' file.
Hydration mismatch between server-rendered and clientYou're rendering context-derived markup before useEffect runs. Render a stable placeholder (null or <p>loading…</p>) on first paint.
Missing host bridge in dev when running next devUse the inline shim trick above, or run sdk-i99dash dev against the static export instead.

On this page