Unify Logo Footer.svg
Unify Applications
Logo
Building a Module

Building a Module

Logo

6 mins READ

Where Modules Live

Every module in an app is listed under the Modules tab of the builder's navigator, kept separate from the app's pages. Opening a module from this tab launches it in the module builder.

A module belongs to the app you created it in. If you need a module available across multiple apps, publish it as a standalone module application.

Creating a New Module

  1. Open an application in the builder.

  2. In the left navigator, click the Modules tab.

  3. Click New module (or the equivalent create action for your platform).

  4. A new, empty module is added to the app and you are taken straight into its builder.

The module belongs to the app from the moment it's created and can immediately be embedded on any page of that app.

The Module Builder Workspace

The module builder gives you the same toolset as the page builder, scoped to the one module:

ElementDescription
CanvasThe center area where you drop and arrange blocks. The module's entire layout lives here.
Left tool railAI Assistant, Design, Data, Hierarchy, Finder, Analytics, Saved Versions, and Settings — the same tools a page has.
Property panelOn the right, for the selected block.
HeaderShows the app name with a Module tag, the module's publish status, a device switcher, and a publish control. A back control returns you to the host app.

Note: A module is a single design surface. The module builder has no page switcher — a module holds one canvas, not a set of pages. When you need to reuse several distinct screens, build several modules, one per screen.

Module Inputs and Outputs

A module is not a sealed box — it exchanges values with wherever it is placed. You declare the contract in the module's settings:

Inputs (inputSchema)

Named values the module accepts from each placement. You declare the input fields (name, type) and each placement supplies its own values. Inside the module, blocks read inputs as:

Output (outputSchema)

Named values the module publishes back to the surrounding page. The module updates these at runtime; the parent page reads them from the Module block's state:

The Module block fires an onOutputChange event whenever the output record changes — this is the single event to react to, regardless of how many output fields you declared.

Outbound events

Custom events the module can emit upward to its parent. Declared in page.events as a list of event definitions, each with an eventType string and a human-readable label.

Inbound events

Custom events the parent can push down into the module. Declared in page.inboundEvents. The parent sends them via the Emit Inbound Event method on the Module block, specifying the eventType and an optional payload.

Warning: Common mistake: The Module block exposes only one event handler: onOutputChange — not one handler per declared outbound event. To tell which module event fired, read the module's output record (which the module updates when it emits).

Embedding a Module on a Page

  1. Open any page in the app builder.

  2. Add a Module block from the block picker.

  3. In the block's properties, select which module to embed.

  4. Under Inputs, bind each input field to a value — a static value, a data source result, or an expression from the surrounding page.

  5. Under Events, wire up onOutputChange to react when the module publishes new output values.

  6. Optionally enable Remember state when hidden if the module should preserve its state across hide/show cycles.

Web and Mobile Modules

The builder matches the app's platform. A module in a web app opens in the web module builder; a module in a mobile app opens in the mobile module builder with controls suited to that platform. The authoring model is the same — canvas, tools, header — but the surface reflects where the module will run.

Publishing a Module

A module has its own publish control in the header. The header shows the current publish status. Publishing makes the module's latest design available to be embedded — including in other applications once it is a published standalone module.

Editing the module changes every placement, because each placement is a live view of the module. There is no per-placement copy to edit in isolation — the per-placement lever is the module's inputs.

Trade-offs & Gotchas

  • Editing the module changes every placement. A change to the module — a new block, a restyle, a data-source swap — shows up in every placement immediately. Plan module changes knowing every placement inherits them.

  • One module = one design surface. The module builder holds a single canvas with no page switcher. If a reusable flow needs several distinct screens, model each as its own module.

  • Input/output/event names must match exactly. The keys used in content.inputs on the placement must match the field names declared in the module's inputSchema. Output keys for reading must match outputSchema field names. There is no compile-time validation — mismatches silently produce undefined.

  • The module's output is read off the block ID, not the module event name. Use {{ moduleBlockId['output']['key'] }} — not the event type string.

Frequently Asked Questions

How do I pass data from the host page into a module?

Declare inputs on the module (in the module's settings under inputSchema), then bind values to those inputs on the Module block's Inputs panel. Inside the module, read them as {{ pageInputs['inputKey'] }}.

How does the module send data back to the host page?

The module publishes an output record (declared in outputSchema). The host page reads it via {{ moduleBlockId['output']['outputKey'] }} and reacts using the onOutputChange event on the Module block — the single event that fires whenever any output value changes.

Can a module contain multiple pages?

No. A module has a single design surface — the module builder has no page switcher. If you need several distinct screens in a reusable unit, build each as a separate module.

What's the difference between a module's outbound events and its output?

Both carry data from module to parent, but differently. The output is a persistent record the parent can bind to and read at any time. Outbound events are discrete signals fired at a specific moment — useful for one-time notifications like "form submitted." On the Module block, both mechanisms produce the single onOutputChange handler; the module typically updates its output record when emitting an event, and the parent distinguishes events by reading the output.

  • Modules Overview — the concept and use cases

  • Packages & Modules Screen — managing modules from the Applications area

  • Custom Navigation Module — using a module as your app's nav bar