Serializes a component tree to JSON and passes it to the platform NIF in a single call. Compose (Android) and SwiftUI (iOS) handle diffing and rendering internally.
Node format
%{
type: :column,
props: %{padding: :space_md, background: :surface},
children: [
%{type: :text, props: %{text: "Hello", text_size: :xl, text_color: :on_surface}, children: []},
%{type: :button, props: %{text: "Tap", on_tap: self()}, children: []}
]
}Token resolution
Atom values for color props, spacing props, radius props, and text sizes are
resolved at render time through the active Mob.Theme and the base palette.
Color props (:background, :text_color, :border_color, :color,
:placeholder_color): resolved via theme semantic tokens first, then the
base palette. E.g. :primary → theme's primary → :blue_500 → 0xFF2196F3.
Spacing props (:padding, :padding_top, etc., :gap, :spacing,
:run_spacing): accept spacing
tokens (:space_xs, :space_sm, :space_md, :space_lg, :space_xl)
that are scaled by theme.space_scale.
Radius props (:corner_radius): accept :radius_sm, :radius_md,
:radius_lg, :radius_pill from the active theme.
Border (currently honored on :box only): set both :border_color
(a color token like :primary or :border) and :border_width (an
integer pt/dp value, e.g. 1). When width is 0 or color is unset, no
border draws.
Text size props (:text_size, :font_size): token atoms (:xl, :lg,
etc.) are multiplied by theme.type_scale.
Font props (:font): an atom (:heading) resolves through the active
theme's fonts map to a platform-specific family name; an unresolved atom
or a raw string passes through unchanged. When the theme declares a
:default font token, any node without its own font: gets it injected
automatically — see Mob.Theme's "Font tokens" section.
Component defaults
When a component's props omit styling keys, the renderer injects sensible defaults from the active theme. Explicit props always win over defaults.
# Gets primary background, white text, medium radius automatically:
%{type: :button, props: %{text: "Save", on_tap: {self(), :save}}, children: []}Style structs
A %Mob.Style{} value under the :style key is merged into the node's
own props before serialisation. Inline props override style values.
Platform blocks
Props scoped to one platform are silently ignored on the other:
props: %{padding: 12, ios: %{padding: 20}}
# iOS sees padding: 20; Android sees padding: 12Injecting a mock NIF
Mob.Renderer.render(tree, :android, MockNIF)
Summary
Functions
Return the set of prop keys resolved as colors (theme token or ARGB integer).
Return the full color palette map (token → ARGB integer).
Render a component tree for the given platform.
Return the text-size scale map (token → float sp).
Functions
@spec color_props() :: [atom()]
Return the set of prop keys resolved as colors (theme token or ARGB integer).
@spec colors() :: %{required(atom()) => non_neg_integer()}
Return the full color palette map (token → ARGB integer).
@spec render(map(), atom(), module() | atom(), atom() | {atom(), :replace}) :: {:ok, :json_tree} | {:error, term()}
Render a component tree for the given platform.
Loads the active Mob.Theme, clears the tap registry, serialises the tree
to JSON, and calls set_root/1 on the NIF. Returns {:ok, :json_tree}.
transition is an atom for the nav animation: :push, :pop, :reset or
:none, defaulting to :none (instant swap).
For a navigation that REPLACES the stack, the router passes
{animation, :replace} instead. The animation half still reaches
set_transition/1 as a plain atom — the vocabulary native matches on stays
closed — and the replacement half is emitted as a "replaces_stack" key on
the JSON root, which tells native the outgoing screen is unreachable and its
retained view tree can be released.
The flag is deliberately NOT folded into the transition atom. That atom's name
reaches Android as a raw string and MainActivity.kt matches
"push"/"pop"/"reset" with an exact when, so any value outside that set
falls through to no animation at all. An unknown JSON key is skipped by every
parser on both platforms, so a host that does not read it keeps its behaviour.
Callers never construct the tuple; Mob.Socket validates the public
:push | :pop | :reset vocabulary.
Return the text-size scale map (token → float sp).