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:

Web

Uses src/app/ with bini-server and bini-export

Desktop

Adds src-tauri/ for Windows, macOS, Linux native binaries

Mobile

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.

FolderPurpose
src/Application source folder
src/appApp Router — file-based routing and layouts
src-tauri/Tauri configuration for desktop & mobile (generated)
publicStatic 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.

FilePurpose
vite.config.tsConfiguration file for Vite and Bini.js
package.jsonProject dependencies and scripts
index.htmlHTML entry point — contains <html> and <body> tags
.envEnvironment variables (should not be tracked)
.env.localLocal environment variables (should not be tracked)
.env.productionProduction environment variables
.env.developmentDevelopment environment variables
.oxlintrc.jsonConfiguration file for Oxlint
.oxfmtrc.jsonConfiguration file for Oxfmt
.gitignoreGit files and folders to ignore
tsconfig.jsonConfiguration file for TypeScript
jsconfig.jsonConfiguration 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.

FileExtensionsPurpose
layout.js .jsx .tsxShared UI that wraps pages and nested layouts
page.js .jsx .tsxA page — defines a public route
loading.js .jsx .tsxLoading UI (Suspense fallback)
not-found.js .jsx .tsxCustom 404 UI
hello.ts.js .tsAPI endpoint in src/app/api/

Note: The <html> and <body> tags are defined in index.html, not in layouts.

Complete project structure

my-app/
├── src/                   ← Application source folder
│   ├── app/
│   │   ├── api/           ← API route handlers
│   │   │   └── hello.ts   → /api/hello
│   │   ├── layout.tsx     ← Root layout (returns <Outlet />)
│   │   ├── page.tsx       ← Home page (/)
│   │   ├── loading.tsx    ← Custom loading UI (optional)
│   │   ├── not-found.tsx  ← Custom 404 page (optional)
│   │   └── globals.css    ← Global styles
│   ├── main.tsx           ← React entry point
│   └── App.tsx            ← Auto-generated — do not edit
├── src-tauri/             ← Tauri configuration (desktop & mobile)
│   ├── Cargo.toml         ← Rust dependencies
│   ├── tauri.conf.json    ← Tauri app configuration
│   ├── src/               ← Rust source code
│   └── gen/               ← Android & iOS projects (generated)
├── public/                ← Static assets
│   ├── favicon.ico
│   ├── apple-touch-icon.png
│   ├── logo.png           ← Source icon for native app icons
│   └── og-image.png
├── index.html             ← HTML entry point with <html> and <body>
├── vite.config.ts         ← Vite configuration
├── .oxlintrc.json         ← Oxlint configuration
├── .oxfmtrc.json          ← Oxfmt configuration
└── package.json

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:

PlatformGenerated FilesPurpose
Webdist/Standard Vite build output
Windowssrc-tauri/Native WebView2 binary with Authenticode signing
macOSsrc-tauri/Native WKWebView app with Developer ID notarization
Linuxsrc-tauri/Native WebKitGTK binary as AppImage
Androidsrc-tauri/gen/android/Native APK/AAB via Tauri's Android backend
iOSsrc-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.

PathURL patternNotes
src/app/layout.tsxRoot layout wraps all routes
src/app/blog/layout.tsxWraps /blog and descendants
src/app/page.tsx/Public route
src/app/about/page.tsx/aboutPublic route
src/app/blog/page.tsx/blogPublic route
src/app/blog/authors/page.tsx/blog/authorsPublic 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.

PathURL 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.

PathURL patternNotes
src/app/(marketing)/page.tsx/Group omitted from URL
src/app/(shop)/cart/page.tsx/cartShare layouts within (shop)
src/app/blog/_components/Post.tsxNot routable; safe place for UI utilities
src/app/blog/_lib/data.tsNot routable; safe place for utils

API Routes

Create API endpoints in src/app/api/. Files export a handler function or Hono app.

PathURL patternNotes
src/app/api/hello.ts/api/helloStatic API endpoint
src/app/api/users/[id].ts/api/users/123Dynamic API endpoint
src/app/api/posts/[...slug].ts/api/posts/2024/helloCatch-all API endpoint
app/api/users/[id].ts
// src/app/api/users/[id].ts
import { Hono } from 'hono'

const app = new Hono()

app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: `User ${id}` })
})

export default app

Component hierarchy

The components defined in special files are rendered in a specific hierarchy:

  1. layout.tsx — wraps all children
  2. loading.tsx — React suspense boundary (if present)
  3. not-found.tsx — 404 UI (only at root level)
  4. page.tsx or nested layout.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.