---
title: "React Router"
description: "Run bknd inside React Router"
tags: ["documentation"]
---

## Installation

To get started with React Router and bknd you can either install the package manually, and follow the descriptions below, or use the CLI starter:

### CLI Starter

Create a new React Router CLI starter project by running the following command:

```sh
  npx bknd create -i react-router
```

### Manual

Create a new React Router project by following the [official guide](https://reactrouter.com/start/framework/installation), and then install bknd as a dependency:

<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>

```bash tab="npm"
npm install bknd
```

```bash tab="pnpm"
pnpm install bknd
```

```bash tab="yarn"
yarn add bknd
```

```bash tab="bun"
bun add bknd
```

</Tabs>

## Configuration

<Callout type="warning">
  When run with Node.js, a version of 22 (LTS) or higher is required. Please
  verify your version by running `node -v`, and
  [upgrade](https://nodejs.org/en/download/) if necessary.
</Callout>

Now create a `bknd.config.ts` file in the root of your project. If you created the project using the CLI starter, this file is already created for you.

```typescript title="bknd.config.ts"
import type { ReactRouterBkndConfig } from "bknd/adapter/react-router";

export default {
  connection: {
    url: "file:data.db",
  },
} satisfies ReactRouterBkndConfig;
```

See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `ReactRouterBkndConfig` type extends the `BkndConfig` type with the following additional properties:

```typescript
type ReactRouterEnv = NodeJS.ProcessEnv;
type ReactRouterFunctionArgs = {
  request: Request;
};
export type ReactRouterBkndConfig<Env = ReactRouterEnv> =
  FrameworkBkndConfig<Env>;
```

## Serve the API

Create a helper file to instantiate the bknd instance and retrieve the API, importing the configurationfrom the `bknd.config.ts` file:

```ts title="app/bknd.ts"
import {
  type ReactRouterBkndConfig,
  getApp as getBkndApp,
} from "bknd/adapter/react-router";
import config from "../bknd.config";

export { config };

// you may adjust this function depending on your runtime environment.
// e.g. when deploying to cloudflare workers, you'd want the FunctionArgs to be passed in
// to resolve environment variables
export async function getApp() {
  return await getBkndApp(config, process.env as any);
}

export async function getApi(
  args?: { request: Request },
  opts?: { verify?: boolean },
) {
  const app = await getApp();
  if (opts?.verify) {
    const api = app.getApi({ headers: args?.request.headers });
    await api.verifyAuth();
    return api;
  }

  return app.getApi();
}
```

For more information about the connection object, refer to the [Database](/usage/database) guide.

Create a new api splat route file at `app/routes/api.$.ts`:

```ts title="app/routes/api.$.ts"
import { getApp } from "~/bknd";

const handler = async (args: { request: Request }) => {
  const app = await getApp();
  return app.fetch(args.request);
};

export const loader = handler;
export const action = handler;
```

## Enabling the Admin UI

Create a new splat route file at `app/routes/admin.$.tsx`:

```tsx title="app/routes/admin.$.tsx"
import { lazy, Suspense, useSyncExternalStore } from "react";
import { type LoaderFunctionArgs, useLoaderData } from "react-router";
import { getApi } from "~/bknd";

const Admin = lazy(() =>
  import("bknd/ui").then((mod) => ({ default: mod.Admin })),
);
import "bknd/dist/styles.css";

export const loader = async (args: LoaderFunctionArgs) => {
  const api = await getApi(args, { verify: true });
  return {
    user: api.getUser(),
  };
};

export default function AdminPage() {
  const { user } = useLoaderData<typeof loader>();
  // derived from https://github.com/sergiodxa/remix-utils
  // @ts-ignore
  const hydrated = useSyncExternalStore(
    () => {},
    () => true,
    () => false,
  );
  if (!hydrated) return null;

  return (
    <Suspense>
      <Admin
        withProvider={{ user }}
        config={{ basepath: "/admin", logo_return_path: "/../" }}
      />
    </Suspense>
  );
}
```

## Example usage of the API

You can use the `getApi` helper function we've already set up to fetch and mutate:

```tsx title="app/routes/_index.tsx"
import { useLoaderData, type LoaderFunctionArgs } from "react-router";
import { getApi } from "~/bknd";

export const loader = async (args: LoaderFunctionArgs) => {
  // use authentication from request
  const api = await getApi(args, { verify: true });
  const { data } = await api.data.readMany("todos");
  return { data, user: api.getUser() };
};

export default function Index() {
  const { data, user } = useLoaderData<typeof loader>();

  return (
    <div>
      <h1>Data</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
      <h1>User</h1>
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </div>
  );
}
```
