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.

Next.js does not run on DiLink 5.0 (L5, L5 Ultra, Song Plus). Its static export is modern chunked ES-module JS; the ~2022 Di5.0 WebView can't execute it and the page blanks with no error. Next.js here is L8 (Di5.1) only. Need L5? Use the Build for L5 + L8 classic-bundle path.

Three pitfalls to know up front:

  1. Di5.0 / L5 is unsupported. Next's /_next/ chunk loader is an ES module; Di5.0 WebViews silently don't run it (see the callout above and Targets matrix).
  2. 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.
  3. 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

That's it — one package. The runtime client + types live at the root entry, the React bindings under the i99dash/react subpath, the CLI at i99dash/cli, the dev-server at i99dash/dev-server. The i99dash binary is on $PATH after install.

i99dash/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/react';
import { createClientOrSSR, type MiniAppClient } from 'i99dash';

export function Providers({ children }: { children: React.ReactNode }) {
  // Start null on both server and client first paint — keeps the
  // hydrated tree byte-identical to SSR. The effect runs once on mount
  // and produces the single real client instance for the app's lifetime.
  const [client, setClient] = useState<MiniAppClient | null>(null);
  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/react';
import type { MiniAppContext } from 'i99dash';

const fallback = {
  userId: '',
  activeCarId: '',
  locale: 'en',
  isDark: false,
  appVersion: '0.0.0',
  appId: '',
} satisfies MiniAppContext;

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

Without i99dash/react (plain SDK)

app/fuel/Fuel.client.tsx:

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

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 — i99dash dev pointing at a static build
pnpm next build && pnpm next export
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 a fetch() against a declared manifest.network origin — 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 i99dash dev against the static export instead.
Blank screen on L5 / L5 Ultra / Song Plus (works on L8)Expected — Next's ES-module output can't run on the Di5.0 WebView. Re-bundle as a classic IIFE per Build for L5 + L8.

On this page