CSR-focused • Self-hostable • Opinionated
Reno Stack is a modern web app starter kit designed for speed, efficiency, and self-hosting.
- ⚛️ React App with Vite – Fast and reliable
- 🚦 Tanstack Router – File-based routing
- 🔐 Better-Auth – Simple authentication
- 🎨 Tailwind + Shadcn – Quick UI building
- 🔗 Type-safe DX – End-to-end type safety
- 🛠️ Self-hostable – Everything is self-hostable
- 🧩 Drizzle ORM – Modern, type-safe ORM
- 📦 PNPM – Efficient package management
.vscode
└─ VSCode settings
apps
├─ server
| ├─ Node server with Hono
| ├─ Type-safe database with Drizzle ORM
| ├─ Type-safe .env via @t3-oss/env-core
| └─ Authentication with Better-Auth
└─ web
├─ React
├─ Vite
├─ Tailwind CSS
├─ React Hook Form
├─ React Query with custom Hono RPC
└─ File-based routing with Tanstack Router
packages
├─ ui
| └─ UI components with Shadcn
└─ validators
└─ Shared Zod schemas
Before diving in, it's recommended to read the sections below for a better understanding of the stack. Here's how to run the example app:
Command | Description |
---|---|
pnpm i |
Install dependencies |
cp .env.example .env |
Configure environment variables |
pnpm db:push |
Push Drizzle schema to the database |
Create a Discord application here. Go to your created application and add the following to Redirects
in OAuth2
settings:
http://localhost:8000/api/auth/callback/discord
Add DISCORD_CLIENT_ID
and DISCORD_CLIENT_SECRET
to .env
. If you change the server's port, update VITE_SERVER_URL
and the redirect URI.
Visit http://localhost:5173
to start building! 🚀
Reno Stack uses Hono RPC and React Query in a relatively unique way. React Query has a feature called QueryOptions which is basically for creating reusable queryFn
and queryKey
s. By taking advantage of this, we've made a custom utility that couples extremely well with Hono RPC. This utility gives you two functions called createHonoQueryOptions
and createHonoMutationOptions
. Here's how you'd use them:
For each route of our application, we'll create a {route}.queries.ts
under a folder named queries
in our web application (these naming conventions are arbitrary and can be changed to anything that you'd like)
Let's say you have a notes
route. In notes.queries.ts
, you would have something like this:
import { client } from "../utils/hono-client";
import {
createHonoQueryOptions,
createHonoMutationOptions,
} from "@reno-stack/hono-react-query";
export const notesQueryOptions = createHonoQueryOptions(
["notes"],
client.notes.$get
);
export const noteByIdQueryOptions = createHonoQueryOptions(
({ param: { id } }) => ["notes", id],
client.notes[":id"].$get
);
export const createNoteMutationOptions = createHonoMutationOptions(
client.notes.$post
);
Use them like this:
const notesQuery = useQuery(notesQueryOptions());
Or in case of a query with parameters:
const noteByIdQuery = useQuery(
noteByIdQueryOptions({ param: { id } }, { enabled: !!id })
);
Invalidate queries safely:
await queryClient.invalidateQueries({ queryKey: notesQueryOptions().queryKey });
Simple as that! all the type safety and error handling are done by the utility.
If you want more information about the utility or why you might need it, check out the hono-react-query repository.
Reno Stack uses Drizzle with PostgreSQL. Set your environment variables in .env
and run pnpm db:push
to apply migrations. Use pnpm db:studio
to view the database schema and data.
It's highly recommended that you check out Better-Auth's documentation for learning more about the library.
In short, you have a default schema file (auth-schema.ts
) in the server app that is generated by Better-Auth and a main entry point called auth.ts
. You can add any strategies that you want such as OAuth2 (Reno Stack's example comes with a Discord OAuth integration), email and password, OTP, etc
Reno Stack comes with a withAuth
middleware that you could use in any of your routes/group of routes. Example usage of a simple notes
router:
export const notes = new Hono<HonoAppContext>()
.post("/", zValidator("json", createNotesSchema), withAuth, async (c) => {
const user = c.var.user;
const { title, content } = await c.req.valid("json");
// ...
Use these commands to create new routes and components:
Command | Description |
---|---|
pnpm create:route <name> |
* Create a new server route |
pnpm ui-add <name> |
Add UI components from Shadcn |
- Note *: For creating a client route, please follow the instructions here.
Reno Stack is heavily inspired by T3 stack and it's Turborepo template, but it takes a different approach to building full-stack web applications. Unlike setups where Next.js handles both the API and client, Reno Stack decouples these components. The frontend is a React application powered by Vite, while the backend is a Hono-powered server. They communicate through type-safe API calls, allowing for faster iteration and a more streamlined development process.
While there is ongoing debate about client-side rendering (CSR) being worse or on par with server-side rendering (SSR) in terms of SEO, Reno Stack is particularly suited for applications where SEO is not a primary concern. This is because features like SSG and SSR are not enabled by default (although they can be added, they're just not in the template at this moment). This approach makes Reno Stack an ideal choice for projects that prioritize speed and simplicity over SEO optimization.
Plans include a CLI tool for generating projects with different technologies.
PRs are welcome. Open an issue or provide feedback!