> ## Documentation Index
> Fetch the complete documentation index at: https://developer.jtl-software.com/llms.txt
> Use this file to discover all available pages before exploring further.

# App Shell & UI Integration

> How to integrate your app with the JTL App Shell using the manifest and AppBridge communication

This guide covers the two runtime systems your Cloud App uses inside the JTL platform:

* **AppBridge**: Runtime SDK that enables bidirectional communication between your app (in an iframe) and the host environment (JTL Hub or ERP Cloud).
* **Platform UI**: React component library for building interfaces that match JTL's design system.

Your app's configuration lives in two manifests. See the [App Manifest](/guides/cloud-apps/app-manifest) for registration (identity, lifecycle, and capabilities) and the [Listing Manifest](/guides/cloud-apps/listing-manifest) for how your app appears to merchants.

For authentication flows, see [Authentication & Login](/guides/cloud-apps/authentication-login). For calling the JTL Cloud and JTL-Wawi APIs, see [Using Platform APIs](/guides/cloud-apps/using-platform-apis).

## AppBridge Communication

The AppBridge is provided by the [`@jtl-software/cloud-apps-core`](https://www.npmjs.com/package/@jtl-software/cloud-apps-core) package. It establishes a secure communication channel between your app's frontend (running in an iframe) and the host JTL environment (JTL Hub or ERP Cloud).

The bridge lets your app request data (like session tokens) and call host methods. Publish/subscribe event handling is in development ([see below](/guides/cloud-apps/app-shell-ui-integration#events)).

### Installation

```bash theme={null}
npm install @jtl-software/cloud-apps-core
```

### Initializing the AppBridge

Initialize the AppBridge **before** rendering your React app, then pass the instance as a prop or store it in context:

```tsx theme={null}
import { createAppBridge } from '@jtl-software/cloud-apps-core';
import { createRoot } from 'react-dom/client';

createAppBridge().then((appBridge) => {
	createRoot(document.getElementById('root')!).render(
		<StrictMode>
			<App appBridge={appBridge} />
		</StrictMode>,
	);
});
```

Initializing before render guarantees the channel is open before any component tries to use it.

<Warning>
  Do not initialize the AppBridge inside a component (e.g., in `useEffect`).
  This can cause race conditions where components render before the bridge is
  ready. Always initialize it at the application entry point.
</Warning>

For Next.js apps that need SSR compatibility, use a dynamic import inside a
client-side provider instead.

<Tip>
  You can use AppBridge in any JavaScript frontend framework, including React,
  Vue, and Angular.
</Tip>

### Communication Flow

The AppBridge creates a two-way channel: your app's `appBridge` instance communicates with a corresponding `hostAppBridge` instance inside the JTL Cloud service.

```mermaid theme={null}
sequenceDiagram
    participant App as App Frontend (iframe)
    participant Bridge as appBridge
    participant Host as hostAppBridge
    participant Service as JTL Cloud Service

    App->>Bridge: Initialize (createAppBridge())
    activate Bridge
    Bridge->>Host: Establish connection
    activate Host
    Host-->>Bridge: Connection ready
    deactivate Host
    Bridge-->>App: Bridge instance ready
    deactivate Bridge

    Note over App, Service: Runtime interactions

    App->>Bridge: method.call("getSessionToken")
    activate Bridge
    Bridge->>Host: Invoke getSessionToken()
    activate Host
    Host->>Service: Execute host logic
    activate Service
    Service-->>Host: Return result
    deactivate Service
    Host-->>Bridge: Forward result
    deactivate Host
    Bridge-->>App: Return session token
    deactivate Bridge
```

All communication is **asynchronous and non-blocking**. Your app continues executing while messages are delivered between the iframe and the host. There are no HTTP requests or polling involved.

### API Reference

The AppBridge provides two main interfaces: **methods** (expose/call) and **events** (publish/subscribe).

<Tabs>
  <Tab title="Methods">
    AppBridge methods provide a request/response channel between your app and the host. There are two directions:

    * **Call host methods** (available today): your app invokes functions the host exposes, like `getSessionToken`. See [Environment-specific Methods](#environment-specific-methods) for the full list.
    * **Expose methods to the host** (in development): your app registers functions that the host can invoke by name, used for things like custom calculations or data lookups.

    ### Calling Host Methods

    Invoke a function provided by JTL. The call returns a promise that resolves with the host's result.

    ```javascript theme={null}
    const sessionToken = await appBridge.method.call("getSessionToken");
    ```

    ### Exposing Methods to the Host

    <Info>
      **In development:** `method.expose` and `method.isExposed` are in active development and not yet available. This section will be updated when the API ships.
    </Info>

    Once available, exposing methods will let the host invoke logic that lives in your app. For example, a `calculateShippingCost` function the ERP calls before showing a quote, or a `validateOrder` hook called before checkout. The shape will follow a `name + handler` registration pattern with a disposer for cleanup, mirroring the event subscription model.

    ### Method API Summary

    The table marks which APIs are available today and which are planned.

    | Method                       | Status         | Description                                    |
    | ---------------------------- | -------------- | ---------------------------------------------- |
    | `method.call(name, ...args)` | Available      | Call a host method. Returns `Promise<T>`.      |
    | `method.expose(name, fn)`    | In development | Register a function the host can call by name. |
    | `method.isExposed(name)`     | In development | Check whether a method is already registered.  |
  </Tab>

  <Tab title="Events">
    AppBridge events cover two directions. One is available today for panels; the other is in development.

    * **`event.subscribe`** (available for panel entity context): listen for events the host publishes. Panels use this to react when the merchant navigates to a different entity, for example `CustomerChanged` on the customer view. See [Reading Panel Context](#reading-panel-context) for the pattern and the available events.
    * **`event.publish`** (in development): send events from your app to the host so it can react to actions your app completes, for example `order:verification:complete`.

    <Info>
      **In development:** Publishing events from your app to the host (`event.publish`), and general app-to-app pub/sub beyond the host entity-context events, are in active development. This section will be updated when they ship.
    </Info>

    Host entity-context events follow a consistent convention: PascalCase, past-tense names (`CustomerChanged`), with object payloads (`{ customerId }`) rather than bare values.
  </Tab>
</Tabs>

### Environment-specific Methods

JTL exposes different built-in methods depending on which environment your app is running in.

<Tabs>
  <Tab title="Setup Environment (JTL Hub)">
    These methods are available when your app loads via the `lifecycle.configurationUrl` during installation:

    | Method            | Parameters | Returns           | Description                                                       |
    | ----------------- | ---------- | ----------------- | ----------------------------------------------------------------- |
    | `getSessionToken` | None       | `Promise<string>` | Returns the current session token (contains user and tenant info) |
    | `setupCompleted`  | None       | `Promise<void>`   | Signals that setup is done and activates the app in the Hub       |
  </Tab>

  <Tab title="ERP Environment (ERP Cloud)">
    These methods are available when your app runs inside the ERP (via menu items or panels):

    | Method                 | Parameters | Returns           | Description                                                                                                                                                   |
    | ---------------------- | ---------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `getSessionToken`      | None       | `Promise<string>` | Returns the current session token                                                                                                                             |
    | `getCurrentTime`       | None       | `Promise<Date>`   | Returns the current time as a Date object                                                                                                                     |
    | `getCurrentCustomerId` | None       | `Promise<string>` | Returns the id of the customer the merchant is viewing. Available to panels on the customer view. Resolves to an empty string when no customer is in context. |
  </Tab>
</Tabs>

<Note>
  `getSessionToken` is available in both environments. It's the primary way your frontend identifies the current user and tenant. For details on verifying session tokens on your backend, see [Authentication & Login](/guides/cloud-apps/authentication-login).
</Note>

### Reading Panel Context

Panels can read the current entity in context. On the customer view, call `getCurrentCustomerId` to read the current customer, and subscribe to the `CustomerChanged` event to be notified when it changes.

```javascript theme={null}
// Read the current customer on load
const customerId = await appBridge.method.call("getCurrentCustomerId");
 
// React to the merchant navigating to a different customer
const unsubscribe = appBridge.event.subscribe(
  "CustomerChanged",
  ({ customerId }) => {
    if (!customerId) return;
 
    // Load data for this customer.
  },
);
 
// Call unsubscribe() when your component unmounts
```

The `CustomerChanged` payload is an object of the form `{ customerId: string }`. It's emitted whenever the merchant selects a customer from the customer list, and not on unrelated views. Guard against an empty `customerId` before loading customer-specific data.

### Naming Conventions

AppBridge APIs follow these conventions:

* Host methods use camelCase verbs, for example `getCurrentCustomerId`.
* Events use PascalCase and describe completed actions, for example `CustomerChanged`.
* Event payloads are always objects, for example `{ customerId }`, rather than primitive values. This lets new fields be added later without breaking existing subscribers.

***

## Platform UI Components

JTL provides a [React Component Library](https://www.npmjs.com/package/@jtl-software/platform-ui-react) that matches the platform's design system. Using these components ensures your app looks and feels consistent with the rest of the JTL interface.

### Installation

```bash theme={null}
npm install @jtl-software/platform-ui-react
```

Add the CSS import to your global stylesheet:

```css theme={null}
@import '@jtl-software/platform-ui-react/index.css';
```

### Available Components

The library includes form controls, layout containers, and typography components. Here's the full list:

**Layout components**

* `Box` , `Grid` , `Stack` , `Layout` , `LayoutSection` , `Card`

**Form components**

* `Button` , `Checkbox` , `Input` , `InputOTP` , `Radio` , `Select` , `Textarea` , `Switch` , `Toggle` , `ToggleGroup` , `FormGroup` , `Form`

**Data display**

* `Text` , `Badge` , `Avatar` , `Table` , `DataTable` , `Progress`

**Navigation**

* `Link` , `Breadcrumb` , `Tab` , `Dropdown`

**Feedback**

* `Alert` , `Dialog` , `AlertDialog` , `Tooltip` , `Skeleton`

**Utility**

* `Icon` , `Separator` , `ScrollArea` , `Collapsible` , `Popover` , `Sheet`

### Usage

Import components directly from the package:

```tsx theme={null}
import { Button, Card, Text } from '@jtl-software/platform-ui-react';

export default function MyComponent() {
	return (
		<Card>
			<Text>Welcome to my JTL App</Text>
			<Button
				onClick={() => console.log('clicked')}
				label='Get Started'
			/>
		</Card>
	);
}
```

## What's Next

<CardGroup cols={2}>
  <Card title="Authentication & Login" icon="key" href="/guides/cloud-apps/authentication-login">
    Implement OAuth 2.0 and session token verification in your app.
  </Card>

  <Card title="Using Platform APIs" icon="database" href="/guides/cloud-apps/using-platform-apis">
    Call the JTL Cloud REST and GraphQL APIs from your backend.
  </Card>

  <Card title="Handling Webhooks" icon="bell" href="/guides/cloud-apps/handling-webhooks">
    Respond to events and AppBridge messages.
  </Card>

  <Card title="Best Practices" icon="star" href="/guides/cloud-apps/best-practices">
    Patterns for AppBridge initialization, error handling, and production
    readiness.
  </Card>
</CardGroup>
