Skip to Content
GuideYour First App

Your First App

Your first Ansiq app should be smaller than the showcase examples, but still real enough to demonstrate the model.

It should include:

  • one App
  • one message path
  • one local reactive state
  • one input widget
  • one visible UI update

Here is a small teaching example:

use ansiq::prelude::*; use ansiq::{run_app, view}; #[derive(Clone, Debug)] enum Message { Submit(String), } #[derive(Default)] struct NotesApp { submitted: String, } impl App for NotesApp { type Message = Message; fn render(&mut self, cx: &mut ViewCtx<'_, Self::Message>) -> Element<Self::Message> { let draft = cx.signal(|| String::new()); let current = draft.get(); view! { <Paragraph text={"Type and press Enter"} /> <Input value={current.clone()} on_change={{ let draft = draft.clone(); move |next| draft.set_if_changed(next) }} on_submit={|next| Some(Message::Submit(next))} /> <Paragraph text={ if self.submitted.is_empty() { "Last submit: (nothing yet)".to_string() } else { format!("Last submit: {}", self.submitted) } } /> } } fn update(&mut self, message: Self::Message, _handle: &RuntimeHandle<Self::Message>) { let Message::Submit(next) = message; self.submitted = next; } } #[tokio::main(flavor = "multi_thread")] async fn main() -> std::io::Result<()> { run_app(NotesApp::default()).await }

The four pieces to notice

App

App owns the application lifecycle.

It has two core responsibilities:

  • render: describe the current UI
  • update: handle messages and mutate app state

render

render does not draw to the terminal directly. It returns an Element tree that the runtime owns and updates.

cx.signal(...)

This creates or restores a stable reactive handle for local interaction state.

In this example it has one clear job:

  • the current input draft lives in the local signal
  • Input.on_change only updates that draft
  • only submitted data is promoted into self.submitted

It is not just a local variable. It is part of the cooperation between component scope, reactivity, and runtime continuity.

update

update turns UI events into app state changes. In this example, pressing Enter produces a Submit message and updates the app’s displayed result.

What actually happens when you type

At a high level:

  1. Input receives keyboard input
  2. on_change updates the signal
  3. the signal updates local UI state
  4. on_submit emits Message::Submit
  5. runtime forwards the message into update
  6. update mutates app state
  7. the next render uses the new state

This shows the basic division of labor:

  • signals for local, in-progress interaction state
  • update for application-level message handling

Why both self and signal exist here

This is one of the first things people ask.

For now, use this rule:

  • self.submitted owns durable application state
  • draft owns local reactive input state

In this example the input does not need to become durable app state until the user submits it. That is why keeping it in a local signal is the more natural starting point.

If the app later needs validation, cross-component sharing, or persistence for the in-progress draft, you can promote it upward at that point.

Next

Continue with Core Concepts.
That page explains why Ansiq is runtime-first and why that matters more than the widgets alone.

Last updated on