A full-stack monorepo starter for XORS projects, featuring a Next.js frontend and Elysia backend, initialized with common directory structures and base technologies to standardize our build approach.
- Biome for linting and formatting
- bun for package management, workspaces, and running scripts
- TypeScript for type safety
- clsx for constructing className strings conditionally
- CVA for organizing variant styles
- Next.js for React framework
- ShadCN for starter components built with Radix+Tailwind
- svgr for transforming SVGs into React components
- Tailwind for CSS
- Vitest for unit tests
- Elysia for the API server (Bun-native, TypeScript-first)
- @elysiajs/cors for CORS handling
- @elysiajs/swagger for API documentation
- Clone this repo
- Install bun
- Run
bun installto install all dependencies (workspaces handle both packages) - Run
npx vercel link+npx vercel env pull web/.env.localto retrieve .env files from Vercel (if any) - Run
bun devto start both frontend and backend:- Frontend: http://localhost:3000
- Backend API: http://localhost:3001
- Swagger Docs: http://localhost:3001/swagger
├── web/ # Next.js frontend
│ ├── app/ # Next.js app directory
│ ├── public/ # Static assets
│ └── ...
├── server/ # Elysia backend
│ └── src/
│ ├── index.ts # Server entry point
│ └── routes/ # API route modules
├── package.json # Root workspace config
└── README.md
/app/components- React components/app/config- App configuration/app/constants- Constant values (breakpoints, themes, URLs)/app/hooks- Custom React hooks/app/fonts- Font files and configuration/app/providers- React context providers/app/types- TypeScript type definitions/app/utils- Utility functions (new additions should include tests)
Core config options can be handled in the web/app/config files. Config options include:
- App Settings (Name, Description, URL)
- Environment settings
See .env.example for required environment variables.
not-found.tsxto customize the 404 page
Font configurations live in /app/fonts. Instances of Google Fonts and locally-served fonts should all be configured in fonts.ts, where you can find additional information about configuring CSS variables and Tailwind styles for each typeface.
We generate React components for icons using SVGR. Just drop raw SVGs into /icon-svg, and run bun run --filter web build-icons to rebuild the icon set in app/components/icons/generated. If you have pre-built or custom icon components, drop them in app/components/icons/. Be aware that components in icons/generated will be overwritten by the build process, so any custom modifications should be done to the source SVGs or in a custom icon component.
ShadCN is a component starter library whose components can all be directly installed locally rather than as an NPM package, so we have the ability to modify the files directly. Each component is installed separately on an as-needed basis. Core components will be installed into @/components/ui
For example, here we install the <Button> component:
cd web && bunx shadcn-ui@latest add button/src/index.ts- Server entry point with middleware setup/src/routes/- API route modules (organized by feature)
Routes are organized as Elysia plugins in /src/routes/. Each route file exports an Elysia instance with a prefix:
import { Elysia, t } from "elysia";
export const exampleRoutes = new Elysia({ prefix: "/example" })
.get("/", () => ({ message: "Hello" }))
.post("/", ({ body }) => body, {
body: t.Object({
name: t.String(),
}),
});Then import and use in index.ts:
import { exampleRoutes } from "./routes/example";
const app = new Elysia().use(exampleRoutes).listen(3001);GET /health- Health check endpointGET /health/ready- Readiness checkGET /users- List all users (example)GET /users/:id- Get user by ID (example)POST /users- Create a user (example)
Swagger documentation is automatically generated and available at http://localhost:3001/swagger when the server is running.
Barrel exports simplify imports and improve code organization. This starter uses them extensively.
By creating an index.ts file for a directory, multiple modules or components can be exported from a single entry point:
index.ts
export * from "@/components/foo";
export * from "@/components/bar";
export * from "@/components/foo-bar";Barrel exports allow multiple modules or components to be imported with a single statement:
import { FooComponent, BarComponent, FooBarComponent } from "@/components";This approach keeps imports concise and avoids specifying individual paths for each module or component.
bun dev # Start both frontend and backend in parallel
bun dev:web # Start only the frontend
bun dev:server # Start only the backend
bun build # Build all packages for production
bun test # Run tests in all packages
bun lint # Lint all packages with Biome
bun lint:fix # Fix lint issues in all packages
bun format # Check formatting in all packages
bun format:fix # Fix formatting issues in all packages
bun type-check # Run TypeScript type checking in all packages
bun clean # Remove all node_modulesbun run --filter web dev # Start Next.js dev server
bun run --filter web turbo # Start dev server with Turbopack
bun run --filter web build # Build for production
bun run --filter web start # Start production server
bun run --filter web test # Run tests
bun run --filter web test:watch # Run tests in watch mode
bun run --filter web build-icons # Generate icon components from SVGsbun run --filter server dev # Start Elysia dev server with hot reload
bun run --filter server build # Build for production
bun run --filter server start # Start production server
bun run --filter server test # Run tests