Install FLIN
FLIN ships as a single binary. One command installs everything you need — the language runtime, FlinDB, FlinUI, 1,675 icons, and all 420+ built-in functions.
curl -fsSL https://flin.sh | bashVerify the installation:
flin --versionYou should see v1.0.0-alpha.2. FLIN supports macOS, Linux, and Windows (WSL).
Create a New App
Scaffold a new project with the flin new command. This creates a ready-to-run app with file-based routing.
flin new my-app && cd my-appThis creates the following structure:
my-app/ ├── app/ │ └── index.flin // Home page → / ├── components/ // Custom components ├── layouts/ │ └── default.flin // Default layout ├── entities/ // Data models └── .env // Environment variables
Files in app/ become routes automatically. app/about.flin becomes /about. Folders create nested paths: app/blog/index.flin becomes /blog.
Write Your First Page
Open app/index.flin and replace its contents with the code below. This single file defines a data model, functions, and a template — a complete todo app.
// 1. Define your data model entity Todo { title: text @required done: bool = false } // 2. State variables (reactive) newTitle = "" // 3. Functions fn add() { if newTitle != "" { todo = Todo { title: newTitle } save todo newTitle = "" } } fn toggle(t: Todo) { t.done = !t.done save t } fn remove(t: Todo) { delete t } // 4. Template — just HTML + {curly braces} <h1>{Icon name="check-square" size="28"} My Todos ({Todo.count})</h1> <form submit={add()}> <input bind={newTitle} placeholder="What needs doing?" /> <button type="submit">Add</button> </form> {if Todo.count == 0} <p>No todos yet. Add one above!</p> {else} {for todo in Todo.all} <div class="todo-item"> <input type="checkbox" checked={todo.done} change={toggle(todo)} /> <span>{todo.title}</span> <button click={remove(todo)}> <Icon name="trash-2" size="14" /> </button> </div> {/for} {/if} <p>{Todo.where(done == true).count} completed, {Todo.where(done == false).count} remaining</p>
Let's break down what's happening:
- Lines 2-5:
entity Tododefines a data model. FLIN automatically addsid,created_at, andupdated_atfields. The@requiredvalidator ensurestitleis never empty. - Line 8:
newTitle = ""creates a reactive variable. When it changes, the template updates automatically. - Lines 11-16:
save todopersists the entity to FlinDB. No SQL, no ORM configuration. - Lines 18-21: Functions that take entities must have type annotations:
t: Todo. - Line 27:
bind={newTitle}creates two-way binding between the input and the variable. - Line 43: Template method chains like
work directly in the template.{Todo.where(done == true).count}
Run the Dev Server
Start the development server with hot reload. Every time you save a .flin file, the page updates automatically.
flin devOpen http://localhost:3000 in your browser. You should see your todo app, ready to use. Try adding a few todos, checking them off, and deleting them.
Edit app/index.flin and save — your browser refreshes instantly. No build step, no waiting.
Explore the Admin Console
Every FLIN app ships with a built-in admin console. No extra setup required — it's embedded in the binary.
Visit http://localhost:3000/_flin to access:
Todo.where(done == false) to see incomplete todos.What's Next?
You've built your first FLIN app. Here's where to go from here: