Skip to main content

Quick Start

This guide will walk you through creating your first Fern application - a simple click counter with buttons. You’ll learn the fundamentals of building UIs with Fern’s widget system.

Prerequisites

Before starting, make sure you have:
  • C++17 compiler (g++ or clang++)
  • CMake 3.10+
  • Fern installed globally (see Installation Guide)
If you haven’t installed Fern yet, follow the installation instructions first.

Create Your First Project

1

Create a new project

Use the Fern CLI to scaffold a new project:
fern sprout my-counter
cd my-counter
This creates a new directory with the basic project structure:
my-counter/
├── lib/
│   └── main.cpp
├── fern.yaml
└── README.md
2

Write your first app

Open lib/main.cpp and replace its contents with this click counter example:
#include <fern/fern.hpp>
#include <iostream>

using namespace Fern;

static int clickCount = 0;
static std::shared_ptr<TextWidget> counterDisplay;

void setupUI() {
    // Counter display
    counterDisplay = Text(Point(0, 0), "Clicks: 0", 3, Colors::White);
    
    // Click button
    auto button = Button(
        ButtonConfig(0, 0, 150, 50, "Click Me!")
            .style(ButtonStyle()
                .normalColor(Colors::Blue)
                .hoverColor(Colors::LightBlue)
                .pressColor(Colors::DarkBlue)
                .textColor(Colors::White)
                .textScale(2)
            )
    );
    button->onClick.connect([]() {
        clickCount++;
        counterDisplay->setText("Clicks: " + std::to_string(clickCount));
        std::cout << "Button clicked " << clickCount << " times!" << std::endl;
    });
    
    // Reset button
    auto resetButton = Button(
        ButtonConfig(0, 0, 100, 40, "Reset")
            .style(ButtonStyle()
                .normalColor(Colors::Red)
                .hoverColor(Colors::LightRed)
                .pressColor(Colors::DarkRed)
                .textColor(Colors::White)
                .textScale(2)
            )
    );
    resetButton->onClick.connect([]() {
        clickCount = 0;
        counterDisplay->setText("Clicks: 0");
        std::cout << "Counter reset!" << std::endl;
    });
    
    // Layout
    std::vector<std::shared_ptr<Widget>> children = {
        counterDisplay,
        SizedBox(0, 30),
        button,
        SizedBox(0, 20),
        resetButton
    };
    
    addWidget(Center(Column(children)));
}

void draw() {
    Draw::fill(Colors::DarkBlue);
}

int main() {
    Fern::initialize();
    setupUI();
    Fern::setDrawCallback(draw);
    Fern::startRenderLoop();
    return 0;
}
3

Run your app

Build and run your application:
fern fire
Your app will compile and launch in a new window!

Understanding the Code

Let’s break down the key components of your first Fern app:

1. State Management

static int clickCount = 0;
static std::shared_ptr<TextWidget> counterDisplay;
We use static variables to maintain state across function calls. The counterDisplay stores a reference to the text widget so we can update it later.

2. Creating Widgets

counterDisplay = Text(Point(0, 0), "Clicks: 0", 3, Colors::White);
The Text widget displays text. Parameters are: position, text content, font size, and color.
auto button = Button(
    ButtonConfig(0, 0, 150, 50, "Click Me!")
        .style(ButtonStyle()
            .normalColor(Colors::Blue)
            .hoverColor(Colors::LightBlue)
            .pressColor(Colors::DarkBlue)
            .textColor(Colors::White)
            .textScale(2)
        )
);
The Button widget uses a builder pattern with ButtonConfig and ButtonStyle to configure appearance and behavior.

3. Event Handling

button->onClick.connect([]() {
    clickCount++;
    counterDisplay->setText("Clicks: " + std::to_string(clickCount));
});
Fern uses a signal-slot system for events. Connect lambda functions to handle clicks and update the UI dynamically.

4. Layout System

std::vector<std::shared_ptr<Widget>> children = {
    counterDisplay,
    SizedBox(0, 30),  // Vertical spacing
    button,
    SizedBox(0, 20),
    resetButton
};

addWidget(Center(Column(children)));
  • Column: Arranges widgets vertically
  • SizedBox: Adds spacing between widgets
  • Center: Centers the entire column on screen

5. Rendering Loop

void draw() {
    Draw::fill(Colors::DarkBlue);
}

int main() {
    Fern::initialize();
    setupUI();
    Fern::setDrawCallback(draw);
    Fern::startRenderLoop();
    return 0;
}
  • Fern::initialize(): Initializes the window (default 800x600)
  • setupUI(): Creates and positions widgets
  • setDrawCallback(): Registers the background drawing function
  • startRenderLoop(): Begins the event loop

Next Steps

Explore Widgets

Learn about Text, Button, Slider, Progress Bar, and more

Master Layouts

Create complex UIs with Row, Column, Padding, and Expanded

Creating Widgets

Build custom widgets for your application

Deploy to Web

Compile your app to WebAssembly for browser deployment

Pro Tips

Hot Reload: During development, use fern fire to automatically rebuild when you save changes.
Window Size: Pass dimensions to Fern::initialize() to customize window size:
Fern::initialize(900, 700);  // 900x700 window
Always store widget references (like counterDisplay) as std::shared_ptr<Widget> to ensure proper memory management.

Common Patterns

Creating Reusable Button Styles

ButtonStyle createModernButton(uint32_t color) {
    ButtonStyle style;
    style.normalColor(color)
         .hoverColor(color + 0x00202020)  // Slightly lighter
         .pressColor(color - 0x00202020)  // Slightly darker
         .textColor(Colors::White)
         .textScale(2)
         .borderRadius(12);
    return style;
}

Custom Colors

const uint32_t MyBlue = 0xFF3B82F6;  // ARGB format
const uint32_t MyGreen = 0xFF10B981;
const uint32_t MyRed = 0xFFEF4444;

Responsive Layouts

int width = Fern::getWidth();
int height = Fern::getHeight();

auto centerWidget = std::make_shared<CenterWidget>(0, 0, width, height);
Congratulations! You’ve built your first Fern application. Continue exploring the documentation to learn more advanced features.

Build docs developers (and LLMs) love