> For the complete documentation index, see [llms.txt](https://docs.in8sync.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.in8sync.com/client-admin-guide/getting-started/advanced-components.md).

# Advanced Components

Custom HTML, JavaScript, CSS, and session-variable management. The escape hatches — use sparingly.

Advanced components are escape hatches: custom code, page-injected styling, and session-variable management. Reach for them when standard blocks can't do what you need — and treat them as the most expensive option to maintain.

{% hint style="warning" %}
Custom code runs in your users' browsers. Test changes in a copy of the workflow before publishing, and keep scripts short — long custom logic in a docs-managed string is fragile.
{% endhint %}

## Components in this category

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>HTML</strong></td><td>Embed custom HTML — videos, third-party widgets, formatted snippets.</td><td><a href="#html">#html</a></td></tr><tr><td><strong>JavaScript</strong></td><td>Run custom JavaScript on a page — at load, before/after submit, on item scan, or as a custom action.</td><td><a href="#javascript">#javascript</a></td></tr><tr><td><strong>CSS</strong></td><td>Inject custom CSS rules into a page — animations, overrides, embedded-content styling.</td><td><a href="#css">#css</a></td></tr><tr><td><strong>Variables</strong></td><td>Edit session variables on a page — drives behaviour for other blocks.</td><td><a href="#variables">#variables</a></td></tr></tbody></table>

## When you'd reach for this category

* **Embedding outside content** (video, map, widget) → HTML.
* **Glue logic between blocks** or pre/post-submit hooks → JavaScript.
* **Page-level styling** beyond the theme builder → CSS.
* **Letting the user pick a context value** (currency, price level, location override) → Variables.

If you find yourself reaching here often, ask whether the pattern deserves a real component — flag it to your implementation team.

## HTML

Drops raw HTML into a page. Use it for embeds (video, maps, third-party widgets), formatted content the standard blocks can't produce, or quick prototypes.

In edit mode, the block shows a card with the title and description so you can identify it on the canvas. At runtime, the HTML you've configured is rendered directly into the page.

### Available in

* All module types

### When to use

* Embedding a YouTube video, Google Maps view, or a Vimeo player.
* Pasting an iframe or third-party widget snippet (chat tools, analytics widgets that need an HTML container, embedded forms).
* Formatted content the existing blocks don't cover (custom tables, definition lists, anchor links).
* Quick prototyping while a proper component is being built.

If you find yourself reaching for HTML often for the same pattern, it's a sign that pattern deserves a real component — flag it to your implementation team.

### Settings

| Setting          | Description                                                  |
| ---------------- | ------------------------------------------------------------ |
| **Title**        | Identifier shown on the editor card. Not visible at runtime. |
| **Description**  | Short note for the editor card. Not visible at runtime.      |
| **HTML Content** | The HTML to render at runtime.                               |

### How it works

* **In the editor**, the block renders a card showing Title + Description, so you can spot it on the canvas without seeing the HTML output.
* **At runtime**, the block renders the HTML Content directly into the DOM (via `dangerouslySetInnerHTML`).
* Style and script tags work the same as in any HTML — but be careful with both (see hint below).

{% hint style="warning" %}
The HTML block renders content as-is. If you embed scripts, they execute. If you paste untrusted content, it can run in your users' browsers. Only embed HTML from trusted sources, and prefer the **JavaScript** block for any logic — it's clearer where the script came from.
{% endhint %}

### Examples

**Embed a video.** Paste a `<iframe>` snippet from YouTube. Set Title to "Welcome video" so the editor card is identifiable.

**Add a custom callout box.** A `<div>` with inline styling for a coloured box, used as a one-off announcement banner on a dashboard.

**Quick branding header.** A small HTML snippet for a logo + tagline in a non-standard layout the Hero block doesn't quite cover.

### Related

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>JavaScript</strong></td><td>For logic and behaviour, not just markup.</td><td><a href="#javascript">#javascript</a></td></tr><tr><td><strong>CSS</strong></td><td>For page-level styling — pair with HTML when you need both.</td><td><a href="#css">#css</a></td></tr><tr><td><strong>Best Practices</strong></td><td>When to reach for advanced components vs standard blocks.</td><td><a href="/client-admin-guide/getting-started/best-practices.md">Best Practices</a></td></tr></tbody></table>

## JavaScript

Lets you attach custom JavaScript to a page. The script can run on a fixed event (page load, before submit, after submit, item scanned) or as a custom callable, with access to session variables and order data.

This is the escape hatch for behaviour the standard blocks don't support. Use it carefully — script errors break the page, and logic here is harder to maintain than configuration on a real component.

### Available in

* All module types

### When to use

* A bit of glue logic between two existing blocks (e.g. when an item is scanned, also play a confirmation sound).
* Pre-submit validation rules that go beyond what the standard validation provides.
* Post-submit side effects (write a log entry, fire a webhook, update a session variable).
* Hooking into Order Form lifecycle events (item scanned, before submit, after submit) without a custom component.

If your script grows beyond a few dozen lines, consider asking for a dedicated component — long-running JavaScript inside a docs-managed string is fragile.

### Settings

| Setting       | Description                                                                                                                          |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **Configure** | Opens the configuration modal. All script options live here — description, when to execute, action handler, and the JavaScript body. |

The configuration modal supports:

* **Description** — short note about what the script does. Shown in the editor.
* **Execute on** — when the script runs. Two modes:
  * *Auto Run (On Mount)* — the script runs automatically when the page or module loads.
  * *Custom Event* — the script runs when a specific page event fires. After choosing this mode, a second selector appears where you pick the event from a list. Available events depend on which blocks are on your page and the module type — examples include when an item is scanned, before order submission, after a successful submission, after a customer is selected, and more.
* **JS Code** — the JavaScript body, written in a code editor with syntax highlighting and line numbers. Has access to session variables and order context as documented in the modal.

### How it works

The script runs in the page's JavaScript context with access to:

* The session variables system (read and write).
* Order data when the trigger is order-related (e.g. `beforeSubmit`, `itemScanned` provide the relevant payload).

When the configured trigger fires, the script body executes once.

{% hint style="warning" %}
A thrown exception in your script can break the page. Always wrap risky logic in `try/catch` and fail safely (log to console, set a session variable for diagnostics, but don't let the page hang).
{% endhint %}

### Examples

**Sound on scan.** Set *Execute on* to **Custom Event** and pick the item-scanned event from the list. Play an audio tone when the event fires. Cashiers get instant feedback when a scan registers.

**Pre-submit guard.** Set *Execute on* to **Custom Event** and pick the before-submit event. Read a session variable, and if it indicates a hold, abort the submission before it fires.

**Webhook on success.** Set *Execute on* to **Custom Event** and pick the after-submit event. Send the completed order ID to an internal logging endpoint when the submission succeeds.

**Custom "leave page" warning.** Some organizations want their own wording (in their own language) on the browser's warning when a cashier tries to navigate away mid-sale, instead of the browser's default message. This is the kind of small behaviour tweak the JavaScript component is meant for — ask In8Sync for help if you need this and aren't comfortable writing the script yourself.

### Related

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>HTML</strong></td><td>Pair with HTML when you need both markup and logic.</td><td><a href="#html">#html</a></td></tr><tr><td><strong>Variables</strong></td><td>Initialise session variables your script reads.</td><td><a href="#variables">#variables</a></td></tr><tr><td><strong>Module Variables</strong></td><td>Reading and setting session variables from script.</td><td><a href="/client-admin-guide/workflow-builder-overview/module-variables.md">Workflow Session Variables</a></td></tr></tbody></table>

## CSS

Injects custom CSS into the page. The styles you configure are added to the document `<head>` when the page renders, and removed when it unmounts.

Use this for page-level visual customisation that the theme builder and component-level styling don't cover — keyframe animations, specific element overrides, or styling for the contents of an HTML block.

### Available in

* All module types

### When to use

* Adding a keyframe animation (e.g. a pulsing badge or sliding banner) the standard blocks don't support.
* Overriding a specific element's appearance on one page only — without touching the global theme.
* Styling the contents of a sibling HTML block (the HTML block doesn't have its own styling controls).
* One-off tweaks during prototyping. For long-term styling needs across many pages, use the theme builder.
* Styles that should apply across every page in a specific workflow — add CSS in the workflow's own settings instead of dropping this block onto each page individually.

For colour, font, and layout changes that should apply across the whole portal, use the theme builder instead — it's designed for that and your changes will be preserved through upgrades.

### Settings

| Setting         | Description                                                  |
| --------------- | ------------------------------------------------------------ |
| **Title**       | Identifier shown on the editor card. Not visible at runtime. |
| **Description** | Short note for the editor card. Not visible at runtime.      |
| **CSS Content** | The CSS rules to inject.                                     |

### How it works

When the page renders:

1. A unique `<style>` element is created and inserted into `<head>` with the configured CSS.
2. The element stays for the lifetime of the page.
3. When the page unmounts, the `<style>` element is removed.

In the editor, the block shows a card with Title + Description so you can identify it on the canvas. At runtime, the block itself renders nothing — the styles are the entire output.

{% hint style="info" %}
Scope your selectors carefully. CSS in this block applies to the whole page (and any pages that share the document, depending on routing). Use specific selectors or class wrappers to avoid affecting unrelated elements.
{% endhint %}

### Examples

**Pulsing badge.** Add a CSS block with a `@keyframes pulse { ... }` rule and a `.pulse-badge` class that applies the animation. Then add `class="pulse-badge"` to a Stats or Card block via inline styling.

**Hide a third-party widget's branding.** A short CSS rule targeting the widget's class to set `display: none`. Useful for embedded widgets via the HTML block that ship with extra branding you don't want.

**Page-specific accent colour.** Override the primary button colour for one specific reports page without affecting the rest of the portal.

### Related

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>HTML</strong></td><td>Often paired with CSS for fully custom presentational content.</td><td><a href="#html">#html</a></td></tr><tr><td><strong>Styling</strong></td><td>Where component-level styling and the theme builder fit in the bigger picture.</td><td><a href="/client-admin-guide/getting-started/styling.md">Styling</a></td></tr><tr><td><strong>Best Practices</strong></td><td>When to reach for advanced components vs theme overrides.</td><td><a href="/client-admin-guide/getting-started/best-practices.md">Best Practices</a></td></tr></tbody></table>

## Variables

Manages session variables on a page. Variables are scoped values (user, workflow, location, subsidiary) that other blocks read to drive their behaviour — pricing, filters, conditional visibility, defaults.

The block itself is mostly invisible to end users. It exposes editable variable controls that users can change, with each change saved back to the appropriate scope.

### Available in

* All module types **except Reports**

### When to use

* A workflow that needs the user to pick a context (e.g. *Currency*, *Price Level*) before the rest of the page loads with the right data.
* Letting users override a default for the duration of their session — for example, a temporary location override on a multi-location POS workflow.
* Wherever you want the user to see and change session-scoped values without writing custom JavaScript.

For variables you want to **initialise on load without a UI**, this block isn't the right fit — those typically come from URL parameters or workflow settings, not from a UI control.

### Settings

| Setting       | Description                                                                                                                                         |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Variables** | List of variable definitions, each with a `key` and a `label`. Opens an editor where you add, remove, and reorder the variables this block exposes. |

Each variable in the list has:

* **Key** — the internal variable name other blocks read (e.g. `currency`, `priceLevel`).
* **Label** — the human-readable label shown next to the input.

### How it works

At runtime:

1. The block reads each configured variable from its scope (user, workflow, location, subsidiary — resolved from session context).
2. Each variable renders as an editable input with its label.
3. When the user changes a value and saves, the new value is persisted to the appropriate scope.
4. Other blocks on the page that read those variable keys re-render with the new values.

{% hint style="info" %}
Variables are scoped — a value set against the *user* scope persists across that user's sessions, while a *workflow* scope value applies to everyone using that workflow. The scope is determined by the block's runtime context, not configured here.
{% endhint %}

### Examples

**Currency selector.** Configure a single variable `{ key: 'currency', label: 'Currency' }`. Place the Variables block at the top of a multi-currency catalog page. The Catalog Display block reads `currency` and filters product prices accordingly.

**Price level override.** Configure `{ key: 'priceLevel', label: 'Price Level' }`. On a B2B order form, this lets the user temporarily switch their pricing tier for the current session.

### Related

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Module Variables</strong></td><td>The full guide to session variables — scopes, sources, and consumers.</td><td><a href="/client-admin-guide/workflow-builder-overview/module-variables.md">Workflow Session Variables</a></td></tr><tr><td><strong>Action Dropdown</strong></td><td>An alternative way to set a variable based on user choice.</td><td><a href="/client-admin-guide/getting-started/action-components.md#action-dropdown">Action Components</a></td></tr><tr><td><strong>JavaScript</strong></td><td>For programmatic variable manipulation beyond simple editing.</td><td><a href="#javascript">#javascript</a></td></tr></tbody></table>

## Related

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Module Variables</strong></td><td>The full guide to session variables — scopes, sources, and consumers.</td><td><a href="/client-admin-guide/workflow-builder-overview/module-variables.md">Workflow Session Variables</a></td></tr><tr><td><strong>Styling</strong></td><td>Where component-level styling and the theme builder fit in the bigger picture.</td><td><a href="/client-admin-guide/getting-started/styling.md">Styling</a></td></tr><tr><td><strong>Best Practices</strong></td><td>Production checklist before publishing custom code.</td><td><a href="/client-admin-guide/getting-started/best-practices.md">Best Practices</a></td></tr></tbody></table>
