Back to flin
flin

Embedded Demo and Templates

Starter templates and the embedded demo that ships with every FLIN installation.

Thales & Claude | March 25, 2026 7 min flin
flintemplatesdemostarteronboarding

The first five minutes with a new language determine whether a developer continues or closes the tab. If flin new myapp produces an empty directory with a config file and a vague README, the developer has to figure out the project structure, the file conventions, the routing model, and the entity syntax before writing a single line of code. That is a wall, not a welcome mat.

FLIN's project templates are embedded directly in the compiled binary. When you run flin new myapp --template fullstack, the CLI writes a complete, runnable application to disk without making any network requests. No npm install. No downloading starter repositories. No waiting for a package registry. The template is already there, compiled into the binary at build time using Rust's include_str! macro.

Three Progressive Templates

The template system offers three levels of complexity, matching the developer's experience with FLIN:

flin new myapp --template starter     # Beginner: counter demo
flin new myapp --template todo        # Intermediate: TodoMVC with entities
flin new myapp --template fullstack   # Advanced: routing, API, auth, i18n

Each template is a self-contained application that demonstrates FLIN's features at the appropriate level of complexity.

The Starter Template

The starter template creates a single-file counter application -- the "Hello World" of reactive frameworks:

myapp/
  app/
    index.flin
  README.md

The index.flin file contains a minimal reactive counter:

count = 0

FLIN Counter

Count: {count}

```

This template teaches three concepts: reactive state (count = 0), view templates (

...
), and event handlers (click={count++}). A developer can run flin dev immediately and see a working application in the browser.

The Todo Template

The todo template introduces entities and data persistence:

myapp/
  app/
    index.flin
  entities/
    Todo.flin
  README.md

The entity file demonstrates FLIN's database system:

entity Todo {
    title: text @required
    completed: bool = false
    created_at: time = now()
}

The index file shows CRUD operations:

todos = Todo.all

fn addTodo(title: text) { todo = Todo { title: title } save todo }

fn toggleTodo(id: int) { todo = Todo.find(id) todo.completed = !todo.completed save todo }

fn deleteTodo(id: int) { todo = Todo.find(id) delete todo }

FLIN TodoMVC

{for todo in todos}
{todo.title}
{/for}
```

This template teaches entities, persistence, form handling, and list rendering. It covers 80% of what most web applications need.

The Fullstack Template

The fullstack template is a complete application with every FLIN feature:

myapp/
  app/
    index.flin
    about.flin
    api/
      users.flin
  components/
    Navbar.flin
    Footer.flin
  entities/
    User.flin
  translations.flin
  flin.config
  README.md

This template demonstrates file-based routing (each .flin file in app/ becomes a route), API routes (files in app/api/), reusable components, entity definitions, internationalization, and configuration. It is the template that shows FLIN replacing an entire stack -- React + Express + Prisma + i18next -- in a handful of files.

Embedding at Compile Time

The templates are embedded in the FLIN binary using Rust's include_str! macro, which reads files at compile time and includes them as string constants:

// src/templates/starter.rs
pub const INDEX_FLIN: &str = include_str!("../../embedded/starter/app/index.flin");
pub const README: &str = include_str!("../../embedded/starter/README.md");

// src/templates/todo.rs pub const INDEX_FLIN: &str = include_str!("../../embedded/todo-app/app/index.flin"); pub const ENTITY_TODO: &str = include_str!("../../embedded/todo-app/entities/Todo.flin"); pub const README: &str = include_str!("../../embedded/todo-app/README.md");

// src/templates/fullstack.rs pub const INDEX_FLIN: &str = include_str!("../../embedded/fullstack/app/index.flin"); pub const ABOUT_FLIN: &str = include_str!("../../embedded/fullstack/app/about.flin"); pub const API_USERS: &str = include_str!("../../embedded/fullstack/app/api/users.flin"); pub const COMP_NAVBAR: &str = include_str!("../../embedded/fullstack/components/Navbar.flin"); pub const COMP_FOOTER: &str = include_str!("../../embedded/fullstack/components/Footer.flin"); pub const ENTITY_USER: &str = include_str!("../../embedded/fullstack/entities/User.flin"); pub const TRANSLATIONS: &str = include_str!("../../embedded/fullstack/translations.flin"); pub const CONFIG: &str = include_str!("../../embedded/fullstack/flin.config"); pub const README: &str = include_str!("../../embedded/fullstack/README.md"); ```

This approach has significant advantages over downloading templates from a registry:

Offline first. Templates work without an internet connection. This matters in environments with unreliable connectivity -- including our home base in Abidjan, where internet outages are a fact of life.

Version consistency. The templates are always compatible with the installed version of FLIN. There is no version mismatch between the CLI and the template it generates.

Zero latency. Project creation is instantaneous. No HTTP requests, no DNS resolution, no waiting for a CDN.

Single binary distribution. The FLIN binary is self-contained. No companion files, no data directories, no assets to manage.

The Scaffolding Functions

Each template has a scaffolding function in src/main.rs that creates the project directory and writes the embedded files:

fn create_starter_project(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    let root = Path::new(name);
    fs::create_dir_all(root.join("app"))?;

fs::write(root.join("app/index.flin"), templates::starter::INDEX_FLIN)?; fs::write(root.join("README.md"), templates::starter::README)?;

println!("Created starter project '{}'", name); println!(" cd {}", name); println!(" flin dev"); Ok(()) }

fn create_fullstack_project(name: &str) -> Result<(), Box> { let root = Path::new(name); fs::create_dir_all(root.join("app/api"))?; fs::create_dir_all(root.join("components"))?; fs::create_dir_all(root.join("entities"))?;

fs::write(root.join("app/index.flin"), templates::fullstack::INDEX_FLIN)?; fs::write(root.join("app/about.flin"), templates::fullstack::ABOUT_FLIN)?; fs::write(root.join("app/api/users.flin"), templates::fullstack::API_USERS)?; fs::write(root.join("components/Navbar.flin"), templates::fullstack::COMP_NAVBAR)?; fs::write(root.join("components/Footer.flin"), templates::fullstack::COMP_FOOTER)?; fs::write(root.join("entities/User.flin"), templates::fullstack::ENTITY_USER)?; fs::write(root.join("translations.flin"), templates::fullstack::TRANSLATIONS)?; fs::write(root.join("flin.config"), templates::fullstack::CONFIG)?; fs::write(root.join("README.md"), templates::fullstack::README)?;

println!("Created fullstack project '{}'", name); println!(" cd {}", name); println!(" flin dev"); Ok(()) } ```

The output messages tell the developer exactly what to do next: change into the directory and run flin dev. Two commands from zero to running application.

Deprecating the Old Demo

Before the template system existed, FLIN had a single --demo flag that generated a project using the old pages/ routing convention. When we introduced the app/ directory convention and the three-tier template system, we deprecated the old demo:

#[deprecated(note = "Use --template starter|todo|fullstack instead")]
pub fn create_demo_app(name: &str) -> Result<(), Box<dyn std::error::Error>> {
    // Old implementation
}

The --demo flag still works as an alias for --template fullstack, ensuring backward compatibility. But the deprecation warning in the code signals to contributors that new development should use the template system.

The Directory Convention

All three templates follow the same directory convention:

app/           # Pages and routes (file-based routing)
app/api/       # API routes (server-side only)
components/    # Reusable UI components
entities/      # Data model definitions
translations.flin  # i18n strings (fullstack only)
flin.config    # Project configuration (fullstack only)

This convention is not arbitrary. It mirrors the structure that FLIN's file-based router expects. Each .flin file in app/ becomes a page route. Each .flin file in app/api/ becomes an API endpoint. Components in components/ are available to all pages without explicit imports.

By teaching this convention through templates rather than documentation, developers learn the project structure by example. They do not need to read a guide about where to put their files -- they see the structure in the generated project and follow the pattern.

Teaching Through Templates

The templates serve double duty: they scaffold projects and they teach the language. Each template is carefully crafted to demonstrate specific FLIN features without overwhelming the developer:

The starter template teaches: reactive state, view templates, event handlers.

The todo template adds: entities, persistence, CRUD operations, form handling, list iteration.

The fullstack template adds: file-based routing, API routes, components, guards, internationalization, configuration.

A developer can progress through the templates as they learn. Start with the counter to understand reactivity. Move to the todo to learn about data. Graduate to the fullstack template when they need routing and APIs. Each step builds on the previous one, introducing new concepts incrementally.

Testing the Templates

The templates themselves are tested indirectly through the CLI integration tests. The flin new command tests verify that project creation succeeds and produces the expected directory structure. Additionally, the template source files are valid FLIN code that passes flin check, ensuring they do not contain syntax errors or type mismatches.

The build also validates the templates at compile time. Since include_str! reads the files during compilation, a missing or renamed template file causes a build failure. This prevents the embarrassing situation where flin new --template todo fails because someone forgot to update the embedded file path.

Templates are the first impression of a language. They must be correct, complete, and instructive. By embedding them in the binary and testing them through the build process, FLIN ensures that every developer's first experience with the language is a positive one.

---

This is Part 176 of the "How We Built FLIN" series, documenting how a CEO in Abidjan and an AI CTO designed and built a programming language from scratch.

Series Navigation: - [175] Documentation Comments in FLIN - [176] Embedded Demo and Templates (you are here) - [177] The FLIN VSCode Extension

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles