---
title: 'React Router'
description: 'Run bknd inside React Router'
---
import InstallBknd from '/snippets/install-bknd.mdx';

## 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:

<Tabs>
   <Tab title="CLI Starter">
      Create a new React Router CLI starter project by running the following command:

      ```sh
      npx bknd create -i react-router
      ```
   </Tab>
   <Tab title="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:

      <InstallBknd />
   </Tab>
</Tabs>


## Serve the API
Create a helper file to instantiate the bknd instance and retrieve the API:

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

const config = {
   connection: {
      url: "file:data.db"
   }
} as const satisfies ReactRouterBkndConfig;

export async function getApp(args?: { request: Request }) {
   return await getBkndApp(config, args);
}

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 app/routes/api.$.ts
import { getApp } from "~/bknd";

const handler = async (args: { request: Request }) => {
   const app = await getApp(args);
   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 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 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>
   );
}
```