Project Structure
Learn the folder and file conventions in Bini.js, and how to organize your project for cross-platform development.
Folder and file conventions
This page provides an overview of all the folder and file conventions in Bini.js, and recommendations for organizing your project across web, desktop, and mobile platforms.
Cross-Platform Project Structure
Bini.js projects are designed to work across all platforms from a single codebase. The same folder structure works for web, desktop, and mobile:
Uses src/app/ with bini-server and bini-export
Adds src-tauri/ for Windows, macOS, Linux native binaries
Adds src-tauri/gen/ for Android and iOS
Top-level folders
Top-level folders are used to organize your application's code and static assets.
| Folder | Purpose |
|---|---|
| src/ | Application source folder |
| src/app | App Router — file-based routing and layouts |
| src-tauri/ | Tauri configuration for desktop & mobile (generated) |
| public | Static assets to be served at root URL |
| dist/ | Production build output (generated) |
Top-level files
Top-level files are used to configure your application, manage dependencies, and define environment variables.
| File | Purpose |
|---|---|
| vite.config.ts | Configuration file for Vite and Bini.js |
| package.json | Project dependencies and scripts |
| index.html | HTML entry point — contains <html> and <body> tags |
| .env | Environment variables (should not be tracked) |
| .env.local | Local environment variables (should not be tracked) |
| .env.production | Production environment variables |
| .env.development | Development environment variables |
| .oxlintrc.json | Configuration file for Oxlint |
| .oxfmtrc.json | Configuration file for Oxfmt |
| .gitignore | Git files and folders to ignore |
| tsconfig.json | Configuration file for TypeScript |
| jsconfig.json | Configuration file for JavaScript |
Routing Files
Add page to expose a route, layout for shared UI such as header, nav, or footer, loading for skeletons, and not-found for custom 404 pages.
| File | Extensions | Purpose |
|---|---|---|
| layout | .js .jsx .tsx | Shared UI that wraps pages and nested layouts |
| page | .js .jsx .tsx | A page — defines a public route |
| loading | .js .jsx .tsx | Loading UI (Suspense fallback) |
| not-found | .js .jsx .tsx | Custom 404 UI |
| hello.ts | .js .ts | API endpoint in src/app/api/ |
Note: The <html> and <body> tags are defined in index.html, not in layouts.
Complete project structure
Note: App.tsx is auto-generated by bini-router. Never edit this file directly.
Platform-Specific Files
When targeting desktop or mobile, Bini.js generates platform-specific files and configurations:
| Platform | Generated Files | Purpose |
|---|---|---|
| Web | dist/ | Standard Vite build output |
| Windows | src-tauri/ | Native WebView2 binary with Authenticode signing |
| macOS | src-tauri/ | Native WKWebView app with Developer ID notarization |
| Linux | src-tauri/ | Native WebKitGTK binary as AppImage |
| Android | src-tauri/gen/android/ | Native APK/AAB via Tauri's Android backend |
| iOS | src-tauri/gen/ios/ | Native app via Tauri's iOS backend |
Nested routes
Folders define URL segments. Nesting folders nests segments. Layouts at any level wrap their child segments. A route becomes public when a page file exists.
| Path | URL pattern | Notes |
|---|---|---|
| src/app/layout.tsx | — | Root layout wraps all routes |
| src/app/blog/layout.tsx | — | Wraps /blog and descendants |
| src/app/page.tsx | / | Public route |
| src/app/about/page.tsx | /about | Public route |
| src/app/blog/page.tsx | /blog | Public route |
| src/app/blog/authors/page.tsx | /blog/authors | Public route |
Dynamic routes
Parameterize segments with square brackets. Use [segment] for a single param, [...segment] for catch‑all, and [[...segment]] for optional catch‑all. Access values via the useParams() hook.
| Path | URL pattern |
|---|---|
| src/app/blog/[slug]/page.tsx | /blog/my-first-post |
| src/app/shop/[...slug]/page.tsx | /shop/clothing, /shop/clothing/shirts |
| src/app/docs/[[...slug]]/page.tsx | /docs, /docs/layouts, /docs/api/use-router |
Route groups and private folders
Organize code without changing URLs with route groups (group), and colocate non-routable files with private folders _folder.
| Path | URL pattern | Notes |
|---|---|---|
| src/app/(marketing)/page.tsx | / | Group omitted from URL |
| src/app/(shop)/cart/page.tsx | /cart | Share layouts within (shop) |
| src/app/blog/_components/Post.tsx | — | Not routable; safe place for UI utilities |
| src/app/blog/_lib/data.ts | — | Not routable; safe place for utils |
API Routes
Create API endpoints in src/app/api/. Files export a handler function or Hono app.
| Path | URL pattern | Notes |
|---|---|---|
| src/app/api/hello.ts | /api/hello | Static API endpoint |
| src/app/api/users/[id].ts | /api/users/123 | Dynamic API endpoint |
| src/app/api/posts/[...slug].ts | /api/posts/2024/hello | Catch-all API endpoint |
Component hierarchy
The components defined in special files are rendered in a specific hierarchy:
layout.tsx— wraps all childrenloading.tsx— React suspense boundary (if present)not-found.tsx— 404 UI (only at root level)page.tsxor nestedlayout.tsx
The components are rendered recursively in nested routes, meaning the components of a route segment will be nested inside the components of its parent segment.
Colocation
In the src/app directory, nested folders define route structure. Each folder represents a route segment that maps to a URL path.
However, even though route structure is defined through folders, a route is not publicly accessible until a page.tsx file is added to a route segment.
This means that project files can be safely colocated inside route segments in the app directory without accidentally being routable.