Working TanStack Router Option with default options (default, tailwind, shadcn) a8abbf2c
Maximilian Leodolter · 2025-08-07 15:44 11 file(s) · +373 −6
src/installers/tanstack-router.ts +71 −1
21 21
		const projectPath = path.resolve(process.cwd(), projectName);
22 22
		spinner.text = "Installing TanStack Router...";
23 23
		await addPackageDependency({
24 -
			dependencies: [""],
24 +
			dependencies: [
25 +
				"@tanstack/react-router",
26 +
				"@tanstack/react-router-devtools",
27 +
			],
25 28
			target: "client",
26 29
			projectName,
27 30
		});
31 +
32 +
		await addPackageDependency({
33 +
			dependencies: ["@tanstack/router-plugin"],
34 +
			devMode: true,
35 +
			target: "client",
36 +
			projectName,
37 +
		});
38 +
39 +
		const viteConfigTemplate = nameGenerator("vite.config.ts", {
40 +
			tailwind,
41 +
			shadcn,
42 +
			tanstackRouter: true,
43 +
		});
44 +
		const viteConfigSrc = path.join(
45 +
			EXTRAS_DIR,
46 +
			"client",
47 +
			"vite.config.ts",
48 +
			viteConfigTemplate,
49 +
		);
50 +
		const viteConfigTarget = path.join(projectPath, "client", "vite.config.ts");
51 +
		fs.copySync(viteConfigSrc, viteConfigTarget);
52 +
53 +
		const rootTsxSrc = path.join(
54 +
			EXTRAS_DIR,
55 +
			"client",
56 +
			"src",
57 +
			"routes",
58 +
			"__root.tsx",
59 +
		);
60 +
		const rootTsxTarget = path.join(
61 +
			projectPath,
62 +
			"client",
63 +
			"src",
64 +
			"routes",
65 +
			"__root.tsx",
66 +
		);
67 +
		fs.copySync(rootTsxSrc, rootTsxTarget);
68 +
69 +
		const indexTsxSrc = path.join(
70 +
			EXTRAS_DIR,
71 +
			"client",
72 +
			"src",
73 +
			"routes",
74 +
			"index.tsx",
75 +
			nameGenerator("index.tsx", { tanstackQuery, tailwind, shadcn, rpc }),
76 +
		);
77 +
		const indexTsxTarget = path.join(
78 +
			projectPath,
79 +
			"client",
80 +
			"src",
81 +
			"routes",
82 +
			"index.tsx",
83 +
		);
84 +
		fs.copySync(indexTsxSrc, indexTsxTarget);
85 +
86 +
		const mainTsxSrc = path.join(
87 +
			EXTRAS_DIR,
88 +
			"client",
89 +
			"src",
90 +
			"main.tsx",
91 +
			nameGenerator("main.tsx", { tanstackQuery, tanstackRouter: true }),
92 +
		);
93 +
		const mainTsxTarget = path.join(projectPath, "client", "src", "main.tsx");
94 +
		fs.copySync(mainTsxSrc, mainTsxTarget);
95 +
96 +
		const appTsxTarget = path.join(projectPath, "client", "src", "App.tsx");
97 +
		fs.remove(appTsxTarget);
28 98
29 99
		// const selectedTemplate = nameGenerator("App.tsx", {
30 100
		// 	rpc,
src/lib/install-packages.ts +6 −5
4 4
import { tanstackQueryInstaller } from "@/installers/tanstack-query";
5 5
import { rpcInstaller } from "@/installers/rpc";
6 6
import { reactRouterInstaller } from "@/installers/react-router";
7 +
import { tanstackRouterInstaller } from "@/installers/tanstack-router";
7 8
8 9
export async function installPackages(
9 10
	options: Required<ProjectOptions>,
20 21
		await setupBiome(projectPath);
21 22
	}
22 23
24 +
	if (tanstackQuery) {
25 +
		await tanstackQueryInstaller(options);
26 +
	}
27 +
23 28
	if (router !== "none") {
24 29
		switch (router) {
25 30
			case "reactrouter": {
27 32
				break;
28 33
			}
29 34
			case "tanstackrouter": {
30 -
				console.log("Instlling TanStack Router");
35 +
				await tanstackRouterInstaller(options);
31 36
				break;
32 37
			}
33 38
		}
34 -
	}
35 -
36 -
	if (tanstackQuery) {
37 -
		await tanstackQueryInstaller(options);
38 39
	}
39 40
40 41
	return false;
src/templates/extras/client/src/main.tsx/main-with-tanstackquery-tanstackrouter.tsx (added) +33 −0
1 +
import { StrictMode } from "react";
2 +
import ReactDOM from "react-dom/client";
3 +
import { RouterProvider, createRouter } from "@tanstack/react-router";
4 +
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5 +
import "./index.css";
6 +
7 +
const queryClient = new QueryClient();
8 +
9 +
// Import the generated route tree
10 +
import { routeTree } from "./routeTree.gen";
11 +
12 +
// Create a new router instance
13 +
const router = createRouter({ routeTree });
14 +
15 +
// Register the router instance for type safety
16 +
declare module "@tanstack/react-router" {
17 +
	interface Register {
18 +
		router: typeof router;
19 +
	}
20 +
}
21 +
22 +
// Render the app
23 +
const rootElement = document.getElementById("root")!;
24 +
if (!rootElement.innerHTML) {
25 +
	const root = ReactDOM.createRoot(rootElement);
26 +
	root.render(
27 +
		<StrictMode>
28 +
			<QueryClientProvider client={queryClient}>
29 +
				<RouterProvider router={router} />
30 +
			</QueryClientProvider>
31 +
		</StrictMode>,
32 +
	);
33 +
}
src/templates/extras/client/src/main.tsx/main-with-tanstackrouter.tsx (added) +28 −0
1 +
import { StrictMode } from "react";
2 +
import ReactDOM from "react-dom/client";
3 +
import { RouterProvider, createRouter } from "@tanstack/react-router";
4 +
import "./index.css";
5 +
6 +
// Import the generated route tree
7 +
import { routeTree } from "./routeTree.gen";
8 +
9 +
// Create a new router instance
10 +
const router = createRouter({ routeTree });
11 +
12 +
// Register the router instance for type safety
13 +
declare module "@tanstack/react-router" {
14 +
	interface Register {
15 +
		router: typeof router;
16 +
	}
17 +
}
18 +
19 +
// Render the app
20 +
const rootElement = document.getElementById("root")!;
21 +
if (!rootElement.innerHTML) {
22 +
	const root = ReactDOM.createRoot(rootElement);
23 +
	root.render(
24 +
		<StrictMode>
25 +
			<RouterProvider router={router} />
26 +
		</StrictMode>,
27 +
	);
28 +
}
src/templates/extras/client/src/routes/__root.tsx (added) +11 −0
1 +
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
2 +
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
3 +
4 +
export const Route = createRootRoute({
5 +
	component: () => (
6 +
		<>
7 +
			<Outlet />
8 +
			<TanStackRouterDevtools />
9 +
		</>
10 +
	),
11 +
});
src/templates/extras/client/src/routes/index.tsx/index-with-shadcn-tailwind.tsx (added) +56 −0
1 +
import { createFileRoute } from "@tanstack/react-router";
2 +
import { useState } from "react";
3 +
import beaver from "../assets/beaver.svg";
4 +
import type { ApiResponse } from "shared";
5 +
import { Button } from "@/components/ui/button";
6 +
7 +
export const Route = createFileRoute("/")({
8 +
	component: Index,
9 +
});
10 +
11 +
const SERVER_URL = import.meta.env.VITE_SERVER_URL || "http://localhost:3000";
12 +
13 +
function Index() {
14 +
	const [data, setData] = useState<ApiResponse | undefined>();
15 +
16 +
	async function sendRequest() {
17 +
		try {
18 +
			const req = await fetch(`${SERVER_URL}/hello`);
19 +
			const res: ApiResponse = await req.json();
20 +
			setData(res);
21 +
		} catch (error) {
22 +
			console.log(error);
23 +
		}
24 +
	}
25 +
26 +
	return (
27 +
		<div className="max-w-xl mx-auto flex flex-col gap-6 items-center justify-center min-h-screen">
28 +
			<a href="https://github.com/stevedylandev/bhvr" target="_blank">
29 +
				<img
30 +
					src={beaver}
31 +
					className="w-16 h-16 cursor-pointer"
32 +
					alt="beaver logo"
33 +
				/>
34 +
			</a>
35 +
			<h1 className="text-5xl font-black">bhvr</h1>
36 +
			<h2 className="text-2xl font-bold">Bun + Hono + Vite + React</h2>
37 +
			<p>A typesafe fullstack monorepo</p>
38 +
			<div className="flex items-center gap-4">
39 +
				<Button onClick={sendRequest}>Call API</Button>
40 +
				<Button variant="secondary" asChild>
41 +
					<a target="_blank" href="https://bhvr.dev">
42 +
						Docs
43 +
					</a>
44 +
				</Button>
45 +
			</div>
46 +
			{data && (
47 +
				<pre className="bg-gray-100 p-4 rounded-md">
48 +
					<code>
49 +
						Message: {data.message} <br />
50 +
						Success: {data.success.toString()}
51 +
					</code>
52 +
				</pre>
53 +
			)}
54 +
		</div>
55 +
	);
56 +
}
src/templates/extras/client/src/routes/index.tsx/index-with-tailwind.tsx (added) +62 −0
1 +
import { createFileRoute } from "@tanstack/react-router";
2 +
import { useState } from "react";
3 +
import beaver from "../assets/beaver.svg";
4 +
import type { ApiResponse } from "shared";
5 +
6 +
export const Route = createFileRoute("/")({
7 +
	component: Index,
8 +
});
9 +
10 +
const SERVER_URL = import.meta.env.VITE_SERVER_URL || "http://localhost:3000";
11 +
12 +
function Index() {
13 +
	const [data, setData] = useState<ApiResponse | undefined>();
14 +
15 +
	async function sendRequest() {
16 +
		try {
17 +
			const req = await fetch(`${SERVER_URL}/hello`);
18 +
			const res: ApiResponse = await req.json();
19 +
			setData(res);
20 +
		} catch (error) {
21 +
			console.log(error);
22 +
		}
23 +
	}
24 +
25 +
	return (
26 +
		<div className="max-w-xl mx-auto flex flex-col gap-6 items-center justify-center min-h-screen">
27 +
			<a href="https://github.com/stevedylandev/bhvr" target="_blank">
28 +
				<img
29 +
					src={beaver}
30 +
					className="w-16 h-16 cursor-pointer"
31 +
					alt="beaver logo"
32 +
				/>
33 +
			</a>
34 +
			<h1 className="text-5xl font-black">bhvr</h1>
35 +
			<h2 className="text-2xl font-bold">Bun + Hono + Vite + React</h2>
36 +
			<p>A typesafe fullstack monorepo</p>
37 +
			<div className="flex items-center gap-4">
38 +
				<button
39 +
					onClick={sendRequest}
40 +
					className="bg-black text-white px-2.5 py-1.5 rounded-md"
41 +
				>
42 +
					Call API
43 +
				</button>
44 +
				<a
45 +
					target="_blank"
46 +
					href="https://bhvr.dev"
47 +
					className="border-1 border-black text-black px-2.5 py-1.5 rounded-md"
48 +
				>
49 +
					Docs
50 +
				</a>
51 +
			</div>
52 +
			{data && (
53 +
				<pre className="bg-gray-100 p-4 rounded-md">
54 +
					<code>
55 +
						Message: {data.message} <br />
56 +
						Success: {data.success.toString()}
57 +
					</code>
58 +
				</pre>
59 +
			)}
60 +
		</div>
61 +
	);
62 +
}
src/templates/extras/client/src/routes/index.tsx/index.tsx (added) +54 −0
1 +
import { createFileRoute } from "@tanstack/react-router";
2 +
import { useState } from "react";
3 +
import beaver from "../assets/beaver.svg";
4 +
import type { ApiResponse } from "shared";
5 +
import "../App.css";
6 +
7 +
export const Route = createFileRoute("/")({
8 +
	component: Index,
9 +
});
10 +
11 +
const SERVER_URL = import.meta.env.VITE_SERVER_URL || "http://localhost:3000";
12 +
13 +
function Index() {
14 +
	const [data, setData] = useState<ApiResponse | undefined>();
15 +
16 +
	async function sendRequest() {
17 +
		try {
18 +
			const req = await fetch(`${SERVER_URL}/hello`);
19 +
			const res: ApiResponse = await req.json();
20 +
			setData(res);
21 +
		} catch (error) {
22 +
			console.log(error);
23 +
		}
24 +
	}
25 +
26 +
	return (
27 +
		<>
28 +
			<div>
29 +
				<a href="https://github.com/stevedylandev/bhvr" target="_blank">
30 +
					<img src={beaver} className="logo" alt="beaver logo" />
31 +
				</a>
32 +
			</div>
33 +
			<h1>bhvr</h1>
34 +
			<h2>Bun + Hono + Vite + React</h2>
35 +
			<p>A typesafe fullstack monorepo</p>
36 +
			<div className="card">
37 +
				<div className="button-container">
38 +
					<button onClick={sendRequest}>Call API</button>
39 +
					<a className="docs-link" target="_blank" href="https://bhvr.dev">
40 +
						Docs
41 +
					</a>
42 +
				</div>
43 +
				{data && (
44 +
					<pre className="response">
45 +
						<code>
46 +
							Message: {data.message} <br />
47 +
							Success: {data.success.toString()}
48 +
						</code>
49 +
					</pre>
50 +
				)}
51 +
			</div>
52 +
		</>
53 +
	);
54 +
}
src/templates/extras/client/vite.config.ts/vite.config-with-shadcn-tailwind-tanstackrouter.ts (added) +22 −0
1 +
import { defineConfig } from "vite";
2 +
import react from "@vitejs/plugin-react";
3 +
import tailwindcss from "@tailwindcss/vite";
4 +
import path from "path";
5 +
import { tanstackRouter } from "@tanstack/router-plugin/vite";
6 +
7 +
export default defineConfig({
8 +
	plugins: [
9 +
		// Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react'
10 +
		tanstackRouter({
11 +
			target: "react",
12 +
			autoCodeSplitting: true,
13 +
		}),
14 +
		react(),
15 +
		tailwindcss(),
16 +
	],
17 +
	resolve: {
18 +
		alias: {
19 +
			"@": path.resolve(__dirname, "./src"),
20 +
		},
21 +
	},
22 +
});
src/templates/extras/client/vite.config.ts/vite.config-with-tailwind-tanstackrouter.ts (added) +16 −0
1 +
import { defineConfig } from "vite";
2 +
import react from "@vitejs/plugin-react";
3 +
import tailwindcss from "@tailwindcss/vite";
4 +
import { tanstackRouter } from "@tanstack/router-plugin/vite";
5 +
6 +
export default defineConfig({
7 +
	plugins: [
8 +
		// Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react'
9 +
		tanstackRouter({
10 +
			target: "react",
11 +
			autoCodeSplitting: true,
12 +
		}),
13 +
		react(),
14 +
		tailwindcss(),
15 +
	],
16 +
});
src/templates/extras/client/vite.config.ts/vite.config-with-tanstackrouter.ts (added) +14 −0
1 +
import { defineConfig } from "vite";
2 +
import react from "@vitejs/plugin-react";
3 +
import { tanstackRouter } from "@tanstack/router-plugin/vite";
4 +
5 +
export default defineConfig({
6 +
	plugins: [
7 +
		// Please make sure that '@tanstack/router-plugin' is passed before '@vitejs/plugin-react'
8 +
		tanstackRouter({
9 +
			target: "react",
10 +
			autoCodeSplitting: true,
11 +
		}),
12 +
		react(),
13 +
	],
14 +
});