# Components

AppShell provides three key components for rendering micro-frontend applications:

* **AppRenderer** - Renders fullpage applications
* **PluginRenderer** - Renders plugin applications in designated placeholders
* **ModalRenderer** - Renders applications in modal dialogs

### <mark style="color:red;">AppRenderer</mark>

Renders fullpage micro-frontends managed by the AppShell. It dynamically inserts the application's iframe into the DOM based on the provided application ID.

#### Usage

```tsx
import { AppRenderer } from '@akinon/app-shell';

const FullPageAppContainer = () => {
  return (
    <div className="fullpage-container">
      <AppRenderer id={1} />
    </div>
  );
};
```

#### Props

| Prop   | Type     | Required | Description                                                              |
| ------ | -------- | -------- | ------------------------------------------------------------------------ |
| `id`   | `number` | Yes      | Unique identifier for the application iframe, application PK may be used |
| `path` | `string` | No       | Path to append to the iframe src URL                                     |

#### With Custom Path

```tsx
// Renders the app at its web_url + /dashboard
<AppRenderer id={1} path="/dashboard" />
```

#### How It Works

1. **Retrieve Iframe** - Accesses AppShell context to get the iframe reference for the given ID
2. **Insert Iframe** - Dynamically inserts the iframe into the container
3. **Style Adjustment** - Ensures iframe occupies full container space

### <mark style="color:red;">PluginRenderer</mark>

Renders plugin-type micro-frontends in specific placeholders. Plugins are designed to be embedded within parts of the application without taking over the entire page.

#### Usage

```tsx
import { PluginRenderer } from '@akinon/app-shell';

const Dashboard = () => {
  return (
    <div className="dashboard">
      <main className="content">
        {/* Main dashboard content */}
      </main>
      
      <aside className="sidebar">
        <PluginRenderer placeholderId="dashboard-sidebar" />
      </aside>
    </div>
  );
};
```

#### Props

| Prop            | Type                | Required | Description                                          |
| --------------- | ------------------- | -------- | ---------------------------------------------------- |
| `placeholderId` | `string`            | Yes      | ID of the placeholder where the plugin should render |
| `params`        | `ApplicationParams` | No       | Additional parameters to pass to the plugin          |

#### ApplicationParams Type

```typescript
interface ApplicationParams {
  [key: string]: string | number | boolean | string[] | number[];
}
```

#### With Parameters

```tsx
<PluginRenderer 
  placeholderId="product-analytics" 
  params={{
    productId: 'prod-123',
    showChart: true,
    metrics: ['views', 'sales', 'revenue']
  }}
/>
```

#### How It Works

1. **Identify Plugin** - Uses `placeholderId` to find the matching plugin from AppShell context
2. **Match Configuration** - Finds the app whose `config.placeholder` matches the `placeholderId`
3. **Embed Iframe** - Inserts the iframe into the placeholder container

#### Placeholder Configuration

When registering apps, plugins must specify their `placeholder`:

```tsx
const registeredApps: RegisteredApp[] = [
  {
    pk: 2,
    name: 'Analytics Widget',
    config: {
      web_url: 'https://analytics.example.com',
      visible_type: 'plugin',
      placeholder: 'dashboard-sidebar' // Matches placeholderId
    },
    is_visible: true,
    is_active: true
  }
];
```

### <mark style="color:red;">ModalRenderer</mark>

Renders fullpage application content within a modal dialog. Used for rich modals that need to display micro-frontend content.

{% hint style="info" %}
The `uuid` is automatically generated by the shell when a client calls `showRichModal` action. The shell creates the uuid via `crypto.randomUUID()` and passes it along with `path`, `context`, `size`, and `closeIconColor` to your modal implementation.
{% endhint %}

#### How It Works

1. **Client triggers modal** - Client calls `actions.showRichModal(path, context, size, closeIconColor)`
2. **Shell generates UUID** - Shell creates a unique identifier with `crypto.randomUUID()`
3. **Shell calls your handler** - Your `showRichModal` function receives `(uuid, path, context, size, closeIconColor)`
4. **Render ModalRenderer** - Use the received uuid and path in `ModalRenderer`

#### Usage

```tsx
import { ModalRenderer } from '@akinon/app-shell';

// In your shell's actions configuration
const actions: ApplicationActions = {
  showRichModal: (uuid, path, context, size, closeIconColor) => {
    // Shell has already generated uuid, you receive it here
    setModalConfig({ uuid, path, context, size, closeIconColor });
  }
};

// In your modal component
const RichModal = ({ config, onClose }) => {
  return (
    <Modal open onClose={onClose}>
      <ModalRenderer 
        uuid={config.uuid}
        path={config.path}
        size={config.size}
      />
    </Modal>
  );
};
```

#### Props

<table><thead><tr><th width="114.203125">Prop</th><th width="195.125">Type</th><th width="113.7890625">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>uuid</code></td><td><code>UUID</code></td><td>Yes</td><td>Unique identifier generated by shell and passed to your <code>showRichModal</code> handler</td></tr><tr><td><code>path</code></td><td><code>string</code></td><td>Yes</td><td>Path to render content within the modal (relative to client's web_url)</td></tr><tr><td><code>size</code></td><td><code>ApplicationModalSize</code></td><td>No</td><td>Size configuration for the modal (defaults to 540x360px)</td></tr></tbody></table>

{% hint style="warning" %}
The `context` parameter is sent to the client via `SET_MODAL_CONTEXT` event and accessible through `useAppClient().modalContext`. The `closeIconColor` is for styling the shell's modal close button.
{% endhint %}

#### ApplicationModalSize Type

```typescript
type ApplicationModalSize = {
  maxWidth?: number | string;
  maxHeight?: number | string;
};
```

#### With Size Configuration

```tsx
<ModalRenderer 
  uuid="modal-uuid-123"
  path="/order/confirmation"
  size={{
    maxWidth: 800,
    maxHeight: '90vh'
  }}
/>
```

### <mark style="color:red;">Complete Example</mark>

Here's how all three components work together in a shell application:

```tsx
import { 
  AppShellProvider, 
  AppRenderer, 
  PluginRenderer, 
  ModalRenderer 
} from '@akinon/app-shell';

const ShellApp = () => {
  const [activeAppId, setActiveAppId] = useState<number | null>(null);
  const [modalConfig, setModalConfig] = useState<ModalConfig | null>(null);

  return (
    <AppShellProvider
      apps={registeredApps}
      navigation={navigation}
      data={sharedData}
      actions={actions}
    >
      <div className="shell-layout">
        {/* Header with plugin slot */}
        <header>
          <Logo />
          <PluginRenderer placeholderId="header-actions" />
        </header>

        {/* Main content area */}
        <main>
          {activeAppId && <AppRenderer id={activeAppId} />}
        </main>

        {/* Sidebar with plugin slot */}
        <aside>
          <PluginRenderer placeholderId="sidebar-widget" />
        </aside>

        {/* Modal for rich content */}
        {modalConfig && (
          <Modal open onClose={() => setModalConfig(null)}>
            <ModalRenderer 
              uuid={modalConfig.uuid}
              path={modalConfig.path}
              size={modalConfig.size}
            />
          </Modal>
        )}
      </div>
    </AppShellProvider>
  );
};
```

### <mark style="color:red;">Styling Considerations</mark>

All renderer components insert iframes that fill their container. Ensure the container has appropriate dimensions:

```css
/* Fullpage container */
.fullpage-container {
  width: 100%;
  height: 100vh;
}

/* Plugin container */
.plugin-container {
  width: 300px;
  height: 400px;
}

/* Modal container */
.modal-container {
  width: 100%;
  height: 100%;
}
```

### <mark style="color:red;">Related</mark>

* Hooks - [useAppShell](/akinon-ui/ui-protocol/shell-application/useappshell.md) hook
* [Configuration](/akinon-ui/ui-protocol/shell-application/configuration.md) - App registration
* [API Reference](/akinon-ui/ui-protocol/shell-application/api-reference.md) - Complete type definitions


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.akinon.com/akinon-ui/ui-protocol/shell-application/components.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
