Project Structure
Small apps can keep everything in one route file. Larger apps are easier to read when you separate UI pages, HTTP controllers, services, and data access.
Default Layout
my-app/
├── flint.toml
├── pages/
│ └── index.flint.ui
├── routes/
│ ├── hello.fl
│ └── tasks.fl
├── components/
│ └── navbar.fl
├── services/
│ └── tasks.fl
└── repositories/
└── tasks.flWhat Each Directory Does
| Directory | Loaded automatically | Purpose |
|---|---|---|
pages/ | Yes, recursively for *.flint.ui | Server-rendered UI pages. |
routes/ | Yes, direct *.fl children only when present | HTTP route handlers and controllers. |
components/ | No | Reusable UI fragments included with @use in .flint.ui pages. |
services/ | No | Business rules included with use. |
repositories/ | No | Data access included with use. |
The common API flow is:
route/controller -> service -> repositoryPages can use the same services, and pull in shared UI fragments from components:
page -> component (UI fragment)
page -> service -> repositoryUI-only projects can omit routes/ entirely and use flint build --static to export upload-ready HTML from pages/**/*.flint.ui.
Manifest
flint.toml defines the project and directory conventions:
[project]
name = "my-app"
version = "0.1.0"
[server]
host = "127.0.0.1"
port = 3000
routes = "routes"
pages = "pages"
services = "services"
repositories = "repositories"
components = "components"Only routes and pages control automatic loading. services, repositories, and components are documented conventions available to tooling and humans; include files from them with use or @use.
Route Modules
Every .fl file directly inside routes/ is compiled as an independent module:
routes/tasks.fl
routes/hello.flEach module gets its own bytecode program, label namespace, function table, and string pool. Routes from every module are registered on one HTTP router.
Nested route directories are not loaded automatically.
Shared Code With use
Use use to inline shared code before compilation:
use "services/tasks.fl"routes/tasks.fl:
use "services/tasks.fl"
section .route
GET "/tasks" -> tasks_controller_list
section .text
tasks_controller_list:
call tasks_service_list
ncall http.json, r0
retservices/tasks.fl:
use "repositories/tasks.fl"
section .text
tasks_service_list:
call tasks_repository_all
retrepositories/tasks.fl:
section .text
tasks_repository_all:
mov r0, "[{\"id\":\"1\",\"title\":\"Buy milk\"}]"
ncallr r0, json.parse, r0
retIncludes are resolved from the project root, not from the current file. Included .fl files must use sections too; put shared functions under section .text.
UI Pages
Pages are loaded recursively from pages/:
pages/index.flint.ui -> /
pages/tasks/index.flint.ui -> /tasks
pages/tasks/[id].flint.ui -> /tasks/:id
pages/admin/index.flint.ui -> /adminPages can include services:
@use "services/tasks.fl"
section .route
GET "/tasks"
section .text
call tasks_service_list
ncallr r1, json.stringify, r0
section .render
window "Tasks"
code r1
endUse .flint.ui pages when you want styled controls and default layout. Use route files when the output is mostly JSON, text, redirects, or request/response control flow.
Naming Rules
After includes are expanded, all labels and functions in a module share one flat namespace.
Prefer names that include the feature and layer:
tasks_controller_get
tasks_service_get
tasks_repository_find_by_id
tasks_get_found
tasks_get_doneAvoid generic names:
found:
done:
error:They are likely to collide after use expansion.
Build Output
flint build compiles all route source and generated page source into portable bytecode at:
dist/<project-name>.flintbcRun it with flint run dist/<project-name>.flintbc.
For static sites, flint build --static writes directory-index HTML files:
dist/index.html
dist/about/index.html
dist/flint.css
dist/flint.jsSee CLI and Manifest for full command details.