initial commit

This commit is contained in:
2026-02-08 20:28:46 +05:30
commit c316b759c3
23 changed files with 1839 additions and 0 deletions

290
README.md Normal file
View File

@@ -0,0 +1,290 @@
Welcome to your new TanStack app!
# Getting Started
To run this application:
```bash
bun install
bun --bun run dev
```
# Building For Production
To build this application for production:
```bash
bun --bun run build
```
## Testing
This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with:
```bash
bun --bun run test
```
## Styling
This project uses CSS for styling.
## Routing
This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`.
### Adding A Route
To add a new route to your application just add another a new file in the `./src/routes` directory.
TanStack will automatically generate the content of the route file for you.
Now that you have two routes you can use a `Link` component to navigate between them.
### Adding Links
To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.
```tsx
import { Link } from "@tanstack/react-router";
```
Then anywhere in your JSX you can use it like so:
```tsx
<Link to="/about">About</Link>
```
This will create a link that will navigate to the `/about` route.
More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).
### Using A Layout
In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `<Outlet />` component.
Here is an example layout that includes a header:
```tsx
import { Outlet, createRootRoute } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import { Link } from "@tanstack/react-router";
export const Route = createRootRoute({
component: () => (
<>
<header>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
</header>
<Outlet />
<TanStackRouterDevtools />
</>
),
})
```
The `<TanStackRouterDevtools />` component is not required so you can remove it if you don't want it in your layout.
More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).
## Data Fetching
There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered.
For example:
```tsx
const peopleRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/people",
loader: async () => {
const response = await fetch("https://swapi.dev/api/people");
return response.json() as Promise<{
results: {
name: string;
}[];
}>;
},
component: () => {
const data = peopleRoute.useLoaderData();
return (
<ul>
{data.results.map((person) => (
<li key={person.name}>{person.name}</li>
))}
</ul>
);
},
});
```
Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters).
### React-Query
React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze.
First add your dependencies:
```bash
bun install @tanstack/react-query @tanstack/react-query-devtools
```
Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`.
```tsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// ...
const queryClient = new QueryClient();
// ...
if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
);
}
```
You can also add TanStack Query Devtools to the root route (optional).
```tsx
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const rootRoute = createRootRoute({
component: () => (
<>
<Outlet />
<ReactQueryDevtools buttonPosition="top-right" />
<TanStackRouterDevtools />
</>
),
});
```
Now you can use `useQuery` to fetch your data.
```tsx
import { useQuery } from "@tanstack/react-query";
import "./App.css";
function App() {
const { data } = useQuery({
queryKey: ["people"],
queryFn: () =>
fetch("https://swapi.dev/api/people")
.then((res) => res.json())
.then((data) => data.results as { name: string }[]),
initialData: [],
});
return (
<div>
<ul>
{data.map((person) => (
<li key={person.name}>{person.name}</li>
))}
</ul>
</div>
);
}
export default App;
```
You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview).
## State Management
Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project.
First you need to add TanStack Store as a dependency:
```bash
bun install @tanstack/store
```
Now let's create a simple counter in the `src/App.tsx` file as a demonstration.
```tsx
import { useStore } from "@tanstack/react-store";
import { Store } from "@tanstack/store";
import "./App.css";
const countStore = new Store(0);
function App() {
const count = useStore(countStore);
return (
<div>
<button onClick={() => countStore.setState((n) => n + 1)}>
Increment - {count}
</button>
</div>
);
}
export default App;
```
One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates.
Let's check this out by doubling the count using derived state.
```tsx
import { useStore } from "@tanstack/react-store";
import { Store, Derived } from "@tanstack/store";
import "./App.css";
const countStore = new Store(0);
const doubledStore = new Derived({
fn: () => countStore.state * 2,
deps: [countStore],
});
doubledStore.mount();
function App() {
const count = useStore(countStore);
const doubledCount = useStore(doubledStore);
return (
<div>
<button onClick={() => countStore.setState((n) => n + 1)}>
Increment - {count}
</button>
<div>Doubled - {doubledCount}</div>
</div>
);
}
export default App;
```
We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating.
Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook.
You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest).
# Demo files
Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed.
# Learn More
You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).

10
bknd.config.ts Normal file
View File

@@ -0,0 +1,10 @@
import type { NextjsBkndConfig } from "bknd/adapter/nextjs";
export default {
connection: {
url: "data.db",
},
options:{
}
} satisfies NextjsBkndConfig;

1059
bun.lock Normal file

File diff suppressed because it is too large Load Diff

38
package.json Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "my-bknd-app",
"private": true,
"type": "module",
"scripts": {
"dev": "vite dev --port 3000",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run"
},
"dependencies": {
"@tanstack/react-devtools": "^0.7.0",
"@tanstack/react-router": "^1.132.0",
"@tanstack/react-router-devtools": "^1.132.0",
"@tanstack/react-router-ssr-query": "^1.131.7",
"@tanstack/react-start": "^1.132.0",
"@tanstack/router-plugin": "^1.132.0",
"bknd": "^0.20.0",
"lucide-react": "^0.561.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"vite-tsconfig-paths": "^6.0.2"
},
"devDependencies": {
"@tanstack/devtools-vite": "^0.3.11",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.2.0",
"@types/node": "^22.10.2",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@vitejs/plugin-react": "^5.0.4",
"jsdom": "^27.0.0",
"typescript": "^5.7.2",
"vite": "^7.1.7",
"vitest": "^3.0.5",
"web-vitals": "^5.1.0"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@@ -0,0 +1,25 @@
{
"short_name": "TanStack App",
"name": "Create TanStack App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

38
src/App.css Normal file
View File

@@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

24
src/bknd.ts Normal file
View File

@@ -0,0 +1,24 @@
import { getApp as getBkndApp } from "bknd/adapter/nextjs";
import config from "../bknd.config";
// import { headers } from "next/headers";
export async function getApi({
headers,
verify,
}: {
verify?: boolean;
headers?: Headers;
}) {
const app = await getBkndApp(config, process.env);
if (verify) {
const api = app.getApi({ headers });
await api.verifyAuth();
return api;
}
return app.getApi();
}
export { config };

12
src/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

104
src/routeTree.gen.ts Normal file
View File

@@ -0,0 +1,104 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
import { Route as ApiSplatRouteImport } from './routes/api.$'
import { Route as AdminSplatRouteImport } from './routes/admin.$'
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const ApiSplatRoute = ApiSplatRouteImport.update({
id: '/api/$',
path: '/api/$',
getParentRoute: () => rootRouteImport,
} as any)
const AdminSplatRoute = AdminSplatRouteImport.update({
id: '/admin/$',
path: '/admin/$',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/admin/$': typeof AdminSplatRoute
'/api/$': typeof ApiSplatRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/admin/$': typeof AdminSplatRoute
'/api/$': typeof ApiSplatRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/admin/$': typeof AdminSplatRoute
'/api/$': typeof ApiSplatRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/admin/$' | '/api/$'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/admin/$' | '/api/$'
id: '__root__' | '/' | '/admin/$' | '/api/$'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AdminSplatRoute: typeof AdminSplatRoute
ApiSplatRoute: typeof ApiSplatRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/api/$': {
id: '/api/$'
path: '/api/$'
fullPath: '/api/$'
preLoaderRoute: typeof ApiSplatRouteImport
parentRoute: typeof rootRouteImport
}
'/admin/$': {
id: '/admin/$'
path: '/admin/$'
fullPath: '/admin/$'
preLoaderRoute: typeof AdminSplatRouteImport
parentRoute: typeof rootRouteImport
}
}
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AdminSplatRoute: AdminSplatRoute,
ApiSplatRoute: ApiSplatRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
import type { getRouter } from './router.tsx'
import type { createStart } from '@tanstack/react-start'
declare module '@tanstack/react-start' {
interface Register {
ssr: true
router: Awaited<ReturnType<typeof getRouter>>
}
}

17
src/router.tsx Normal file
View File

@@ -0,0 +1,17 @@
import { createRouter } from '@tanstack/react-router'
// Import the generated route tree
import { routeTree } from './routeTree.gen'
// Create a new router instance
export const getRouter = () => {
const router = createRouter({
routeTree,
context: {},
scrollRestoration: true,
defaultPreloadStaleTime: 0,
})
return router
}

55
src/routes/__root.tsx Normal file
View File

@@ -0,0 +1,55 @@
import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
import { TanStackDevtools } from '@tanstack/react-devtools'
import appCss from '../styles.css?url'
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
{
title: 'TanStack Start Starter',
},
],
links: [
{
rel: 'stylesheet',
href: appCss,
},
],
}),
shellComponent: RootDocument,
})
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
{children}
<TanStackDevtools
config={{
position: 'bottom-right',
}}
plugins={[
{
name: 'Tanstack Router',
render: <TanStackRouterDevtoolsPanel />,
},
]}
/>
<Scripts />
</body>
</html>
)
}

43
src/routes/admin.$.tsx Normal file
View File

@@ -0,0 +1,43 @@
import { getApi } from "@/bknd";
import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { getRequestHeaders } from "@tanstack/react-start/server";
import "bknd/dist/styles.css";
import { Admin } from "bknd/ui";
export const getUser = createServerFn({ method: "GET" }).handler(async () => {
const headers = getRequestHeaders();
const api = await getApi({ verify: true, headers });
return { user: api.getUser() };
});
export const Route = createFileRoute("/admin/$")({
component: RouteComponent,
loader: async () => {
const user = await getUser();
return { user };
},
});
function RouteComponent() {
const { user } = Route.useLoaderData();
console.log(user);
return (
<Admin
withProvider={{
user: {
email: "ada@example.com",
id: "",
strategy: "",
},
}}
config={{
basepath: "/admin",
logo_return_path: "/../",
theme: "system",
}}
baseUrl="http://localhost:3000"
/>
);
}

25
src/routes/api.$.ts Normal file
View File

@@ -0,0 +1,25 @@
import { createFileRoute } from "@tanstack/react-router";
import { config } from "@/bknd";
import { serve } from "bknd/adapter/nextjs";
const handler = serve({
...config,
// cleanRequest: {
// depending on what name you used for the catch-all route,
// you need to change this to clean it from the request.
// searchParams: ["$"],
// },
});
export const Route = createFileRoute("/api/$")({
server: {
handlers: {
ANY: async ({ request }) => {
const res = await handler(request);
// console.log("[API] ", res);
return res;
},
},
},
});

27
src/routes/index.tsx Normal file
View File

@@ -0,0 +1,27 @@
import { createFileRoute, Link } from "@tanstack/react-router";
import "../App.css";
export const Route = createFileRoute("/")({ component: App });
function App() {
return (
<div className="App">
<header className="App-header">
<img
src="/tanstack-circle-logo.png"
alt="TanStack Logo"
style={{
width: "100px",
height: "100px",
}}
/>
<Link
className="App-link"
to="/admin"
>
Admin Dashboard
</Link>
</header>
</div>
);
}

14
src/styles.css Normal file
View File

@@ -0,0 +1,14 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

28
tsconfig.json Normal file
View File

@@ -0,0 +1,28 @@
{
"include": ["**/*.ts", "**/*.tsx"],
"compilerOptions": {
"target": "ES2022",
"jsx": "react-jsx",
"module": "ESNext",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"types": ["vite/client"],
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": false,
"noEmit": true,
/* Linting */
"skipLibCheck": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

26
vite.config.ts Normal file
View File

@@ -0,0 +1,26 @@
import { defineConfig } from 'vite'
import { devtools } from '@tanstack/devtools-vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
import viteTsConfigPaths from 'vite-tsconfig-paths'
import { fileURLToPath, URL } from 'url'
const config = defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
plugins: [
devtools(),
// this is the plugin that enables path aliases
viteTsConfigPaths({
projects: ['./tsconfig.json'],
}),
tanstackStart(),
viteReact(),
],
})
export default config