This commit is contained in:
Aman Varshney
2025-04-14 21:45:28 +05:30
parent 8b03441909
commit 7f441ef670
268 changed files with 3513 additions and 3039 deletions

View File

@@ -0,0 +1,17 @@
import { defineConfig } from "@tanstack/react-start/config";
import viteTsConfigPaths from "vite-tsconfig-paths";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
tsr: {
appDirectory: "src",
},
vite: {
plugins: [
viteTsConfigPaths({
projects: ["./tsconfig.json"],
}),
tailwindcss(),
],
},
});

View File

@@ -0,0 +1,49 @@
{
"name": "tanstack-start",
"private": true,
"type": "module",
"scripts": {
"start": "vinxi start",
"build": "vinxi build",
"serve": "vite preview",
"dev": "vinxi dev --port=3001"
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@tanstack/react-form": "^1.0.5",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/react-query": "^5.71.10",
"@tanstack/react-router": "^1.114.3",
"@tanstack/react-router-with-query": "^1.114.3",
"@tanstack/react-start": "^1.114.3",
"@tanstack/router-plugin": "^1.114.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.473.0",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sonner": "^2.0.3",
"tailwindcss": "^4.1.3",
"tailwind-merge": "^2.6.0",
"tw-animate-css": "^1.2.5",
"vinxi": "^0.5.3",
"vite-tsconfig-paths": "^5.1.4"
},
"devDependencies": {
"@tanstack/react-router-devtools": "^1.114.3",
"@tanstack/react-query-devtools": "^5.71.10",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.2.0",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"jsdom": "^26.0.0",
"typescript": "^5.7.2",
"vite": "^6.1.0",
"web-vitals": "^4.2.4"
}
}

View File

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

View File

@@ -0,0 +1,6 @@
import {
createStartAPIHandler,
defaultAPIFileRouteHandler,
} from "@tanstack/react-start/api";
export default createStartAPIHandler(defaultAPIFileRouteHandler);

View File

@@ -0,0 +1,8 @@
import { StartClient } from "@tanstack/react-start";
import { hydrateRoot } from "react-dom/client";
import { createRouter } from "./router";
const router = createRouter();
hydrateRoot(document, <StartClient router={router} />);

View File

@@ -0,0 +1,102 @@
import {
QueryCache,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { createRouter as createTanstackRouter } from "@tanstack/react-router";
import Loader from "./components/loader";
import "./index.css";
import { routeTree } from "./routeTree.gen";
{{#if (eq api "trpc")}}
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
import { toast } from "sonner";
import type { AppRouter } from "../../server/src/routers";
import { TRPCProvider } from "./utils/trpc";
{{/if}}
{{#if (eq api "orpc")}}
import { orpc, ORPCContext, queryClient } from "./utils/orpc";
{{/if}}
{{#if (eq api "trpc")}}
export const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: (error) => {
toast.error(error.message, {
action: {
label: "retry",
onClick: () => {
queryClient.invalidateQueries();
},
},
});
},
}),
defaultOptions: { queries: { staleTime: 60 * 1000 } },
});
const trpcClient = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
{{#if (includes frontend 'next')}}
url: `${process.env.NEXT_PUBLIC_SERVER_URL}/trpc`,
{{else}}
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
{{/if}}
{{#if auth}}
fetch(url, options) {
return fetch(url, {
...options,
credentials: "include",
});
},
{{/if}}
}),
],
});
const trpc = createTRPCOptionsProxy({
client: trpcClient,
queryClient: queryClient,
});
{{/if}}
export const createRouter = () => {
const router = createTanstackRouter({
routeTree,
scrollRestoration: true,
defaultPreloadStaleTime: 0,
{{#if (eq api "trpc")}}
context: { trpc, queryClient },
{{/if}}
{{#if (eq api "orpc")}}
context: { orpc, queryClient },
{{/if}}
defaultPendingComponent: () => <Loader />,
defaultNotFoundComponent: () => <div>Not Found</div>,
Wrap: ({ children }) => (
<QueryClientProvider client={queryClient}>
{{#if (eq api "trpc")}}
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{children}
</TRPCProvider>
{{/if}}
{{#if (eq api "orpc")}}
<ORPCContext.Provider value={orpc}>
{children}
</ORPCContext.Provider>
{{/if}}
</QueryClientProvider>
),
});
return router;
};
// Register the router instance for type safety
declare module "@tanstack/react-router" {
interface Register {
router: ReturnType<typeof createRouter>;
}
}

View File

@@ -0,0 +1,81 @@
import { Toaster } from "@/components/ui/sonner";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import {
HeadContent,
Outlet,
Scripts,
createRootRouteWithContext,
useRouterState,
} from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import Header from "../components/header";
import appCss from "../index.css?url";
import type { QueryClient } from "@tanstack/react-query";
{{#if (eq api "trpc")}}
import type { TRPCOptionsProxy } from "@trpc/tanstack-react-query";
import type { AppRouter } from "../../../server/src/routers";
{{/if}}
{{#if (eq api "orpc")}}
import type { orpc } from "@/utils/orpc";
{{/if}}
import Loader from "@/components/loader";
{{#if (eq api "trpc")}}
export interface RouterAppContext {
trpc: TRPCOptionsProxy<AppRouter>;
queryClient: QueryClient;
}
{{/if}}
{{#if (eq api "orpc")}}
export interface RouterAppContext {
orpc: typeof orpc;
queryClient: QueryClient;
}
{{/if}}
export const Route = createRootRouteWithContext<RouterAppContext>()({
head: () => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: "My App",
},
],
links: [
{
rel: "stylesheet",
href: appCss,
},
],
}),
component: RootDocument,
});
function RootDocument() {
const isFetching = useRouterState({ select: (s) => s.isLoading });
return (
<html lang="en" className="dark">
<head>
<HeadContent />
</head>
<body>
<div className="grid h-svh grid-rows-[auto_1fr]">
<Header />
{isFetching ? <Loader /> : <Outlet />}
</div>
<Toaster richColors />
<TanStackRouterDevtools position="bottom-left" />
<ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
<Scripts />
</body>
</html>
);
}

View File

@@ -0,0 +1,99 @@
{{#if (eq api "trpc")}}
import { useTRPC } from "@/utils/trpc";
{{/if}}
{{#if (eq api "orpc")}}
import { useORPC } from "@/utils/orpc";
{{/if}}
import { useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: HomeComponent,
});
const TITLE_TEXT = `
██████╗ ███████╗████████╗████████╗███████╗██████╗
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
██║ ███████╗ ██║ ███████║██║ █████╔╝
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
`;
function HomeComponent() {
{{#if (eq api "trpc")}}
const trpc = useTRPC();
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
{{/if}}
{{#if (eq api "orpc")}}
const orpc = useORPC();
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
{{/if}}
return (
<div className="container mx-auto max-w-3xl px-4 py-2">
<pre className="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
<div className="grid gap-6">
<section className="rounded-lg border p-4">
<h2 className="mb-2 font-medium">API Status</h2>
<div className="flex items-center gap-2">
<div
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
/>
<span className="text-muted-foreground text-sm">
{healthCheck.isLoading
? "Checking..."
: healthCheck.data
? "Connected"
: "Disconnected"}
</span>
</div>
</section>
<section>
<h2 className="mb-3 font-medium">Core Features</h2>
<ul className="grid grid-cols-2 gap-3">
<FeatureItem
title="Type-Safe API"
description="End-to-end type safety with tRPC"
/>
<FeatureItem
title="Modern React"
description="TanStack Router + TanStack Query"
/>
<FeatureItem
title="Fast Backend"
description="Lightweight Hono server"
/>
<FeatureItem
title="Beautiful UI"
description="TailwindCSS + shadcn/ui components"
/>
</ul>
</section>
</div>
</div>
);
}
function FeatureItem({
title,
description,
}: {
title: string;
description: string;
}) {
return (
<li className="border-primary border-l-2 py-1 pl-3">
<h3 className="font-medium">{title}</h3>
<p className="text-muted-foreground text-sm">{description}</p>
</li>
);
}

View File

@@ -0,0 +1,12 @@
import { getRouterManifest } from "@tanstack/react-start/router-manifest";
import {
createStartHandler,
defaultStreamHandler,
} from "@tanstack/react-start/server";
import { createRouter } from "./router";
export default createStartHandler({
createRouter,
getRouterManifest,
})(defaultStreamHandler);

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": true,
"noEmit": true,
/* Linting */
"skipLibCheck": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}