Skip to content
Development
Skill

/makepad-2.0-splash

CRITICAL: Use for Makepad 2.0 Splash scripting language. Triggers on: splash language, makepad script, script_mod!, makepad scripting, splash 脚本, makepad 2.0 script, mod.state, on_render, script_eval, streaming evaluation, splash syntax, splash vm, let binding, splash functions,

From plugin
makepad-skills
75214 skills

Claude Code

$ npx -y skills add ZhangHanDong/makepad-skills --skill makepad-2.0-splash --agent claude-code

SKILL.md

makepad-2.0-splash.SKILL.md
name: makepad-2.0-splash
description: |
  CRITICAL: Use for Makepad 2.0 Splash scripting language. Triggers on:
  splash language, makepad script, script_mod!, makepad scripting, splash 脚本,
  makepad 2.0 script, mod.state, on_render, script_eval, streaming evaluation,
  splash syntax, splash vm, let binding, splash functions, hot reload, live reload,
  ScriptModKey, script_mod_overrides, checkpoint, incremental parsing,
  canvas splash, POST splash, fn tick, on_audio, set_text, tab switching,
  音乐播放器, token monitor, driver script, audio API,
  热重载, 脚本引擎, 增量解析

Makepad 2.0 Splash Scripting Language

Splash is Makepad 2.0's core runtime UI scripting language, released February 12, 2026. It replaces the old compile-time `live_design!` macro system with a runtime `script_mod!` macro that enables hot reload, streaming evaluation, and AI-first code generation.

Core Concepts

Script Structure

Every Splash script starts with a `use` import and is embedded in Rust via the `script_mod!{}` macro:

use makepad_widgets::*;

app_main!(App);

script_mod! {
    use mod.prelude.widgets.*

    // let bindings, functions, state, and UI definitions go here

    startup() do #(App::script_component(vm)){
        ui: Root{
            main_window := Window{
                window.inner_size: vec2(800, 600)
                body +: {
                    // UI content
                }
            }
        }
    }
}

Syntax Rules

  • **No commas** between properties -- whitespace-delimited
  • **No semicolons** -- cleaner syntax optimized for LLM generation
  • **Property assignment**: `key: value`
  • **Dot-path shorthand**: `draw_bg.color: #f00` (equivalent to `draw_bg +: { color: #f00 }`)
  • **Merge operator**: `key +: { ... }` extends parent without replacing
  • **Named children**: `name := Widget{...}` (addressable, overridable per-instance)
  • **Let bindings**: `let MyTemplate = Widget{...}` (local scope, must be defined before use)
  • **Rust binding**: `#(Struct::register_widget(vm))` connects Splash to Rust structs
  • **Debug logging**: `~expression` logs value during evaluation

State Management

State is managed via the `mod.state` object and reactive `on_render` callbacks:

// Define state
let state = { counter: 0 }
mod.state = state

// Reactive rendering -- re-runs when .render() is called
main_view := View{
    on_render: ||{
        Label{ text: "Count: " + state.counter }
    }
}

Event Handling

Events are handled both inline in Splash and from Rust:

// Inline event handlers in Splash
add_button := Button{
    text: "Add"
    on_click: ||{
        add_todo(ui.todo_input.text(), "")
        ui.todo_input.set_text("")
    }
}

// TextInput return key
todo_input := TextInput{
    on_return: || ui.add_button.on_click()
}

// Startup event
on_startup: ||{
    ui.main_view.render()
}

From Rust, use `script_eval!` to execute Splash code:

impl MatchEvent for App {
    fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
        if self.ui.button(cx, ids!(increment_button)).clicked(actions) {
            script_eval!(cx, {
                mod.state.counter += 1
                ui.main_view.render()
            });
        }
    }
}

Functions

fn tag_color(tag) {
    if tag == "dev" theme.color_highlight
    else if tag == "design" theme.color_selection_focus
    else theme.color_highlight
}

fn add_todo(text, tag) {
    todos.push({text: text, tag: tag, done: false})
    ui.todo_list.render()
}

Control Flow

// If/else
if todos.len() == 0
    EmptyState{}
else for i, todo in todos {
    TodoItem{ label.text: todo.text }
}

// For loops
for i, item in array {
    Label{ text: item.name }
}

// While
while condition { ... }

HTTP Requests

let req = net.HttpRequest{
    url: "https://api.example.com/data"
    method: net.HttpMethod.GET
    headers: {"User-Agent": "MakepadApp/1.0"}
}
net.http_request(req) do net.HttpEvents{
    on_response: |res| {
        let text = res.body.to_string()
        let json = res.body.parse_json()
    }
    on_error: |e| { /* handle error */ }
}

Streaming responses use `is_streaming: true` with `on_stream` and `on_complete` callbacks.

HTML Parsing

let doc = html_string.parse_html()
doc.query("p")              // all <p> elements
doc.query("#main")           // by id
doc.query("p.bold")          // by class
doc.query("div > p")         // direct children
doc.query("p[0]").text       // text content
doc.query("a@href")          // attribute value

Streaming Evaluation

Splash's parser supports checkpoint-based incremental parsing, designed for AI/LLM streaming code generation:

// Rust API for streaming evaluation
vm.eval_with_append_source(script_mod, &code, NIL.into())

This enables real-time UI updates as code is generated token-by-token, without requiring a complete script before evaluation.

Hot Reload & Script Mod Tracking

Splash scripts support hot reload via the `--hot` flag. The VM tracks each `script_mod!` block with a unique `ScriptModKey` (file, line, column):

// Internal: ScriptModKey uniquely identifies a script_mod! block
ScriptModKey { file: "src/app.rs", line: 5, col: 1 }

// Runtime substitution via overrides
ScriptCode::script_mod_overrides  // HashMap of ScriptModKey -> updated source

**How hot reload works:** 1. File watcher (`makepad_live_reload_core`) detects source file changes 2. `script_mod!` blocks are extracted from Rust source (handles raw strings, comments, char literals) 3. Rust placeholder counts (`#(...)`) are tracked -- adding/removing placeholders requires full rebuild 4. Validated script mods are applied via `script_mod_overrides` 5. IP-to-location mapping provides source maps for error reporting (fallback to nearest token for synthetic opcodes)

**ScriptSource variants:**

  • `ScriptSource::Mod` -- Standard module evaluation (startup)
  • `ScriptSource::Streaming` -- Incremental streaming
Read more
@zhanghandong
Ships withmakepad-skills

Skills for building cross-platform UI applications with Makepad 2.0.

Stars on GitHub

Momentum

stars in total
752
Star history of ZhangHanDong/makepad-skills on GitHub, a chart by star-history.com

Other skills on makepad-skills.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about makepad-2.0-splash.

Which coding agents can use makepad-2.0-splash?
makepad-2.0-splash installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.