---
sidebar_position: 12
title: "Runtime Tags (ui:*): read component tags client-side"
sidebar_label: Runtime Tags (ui:*)
slug: /learn/ui-tags
description: Read a component's ui:-prefixed tags in your host app at runtime with getComponentTags and component.uiTags. Every other tag stays private.
keywords: [myop tags, ui tags, runtime tags, client-side tags, read tags in the browser, getComponentTags, uiTags, component metadata, feature flags]
---
# Runtime Tags (`ui:*`)

Tags are labels you put on a component in the dashboard, the CLI or the MCP tools. Most tags are
for your team: ownership, status, internal system names. **Tags that start with `ui:`** are also
readable by your host app at runtime. You can use them to switch behaviour in your app without a
code change, for example by showing a "beta" badge, picking a layout or tagging analytics events.

```typescript
import { preloadComponents, getComponentTags } from '@myop/sdk/helpers';

await preloadComponents(['my-component-id']);
getComponentTags('my-component-id'); // ['ui:beta', 'ui:layout-wide']
```

## The rule: only `ui:` leaves Myop

| Tag on the component | Readable at runtime? |
|---|---|
| `ui:beta` | ✅ yes |
| `ui:layout-wide` | ✅ yes |
| `UI:beta`, `Ui:beta` | ❌ no (the prefix is lowercase and case-sensitive) |
| `uibeta`, `ui-beta`, `ui` | ❌ no (the colon is required) |
| `system ABC`, `owner:payments`, `internal` | ❌ no, never |

Myop's servers apply this filter before anything reaches the browser. Tags without the `ui:`
prefix are never sent to the SDK, and no query parameter or SDK option can change that.

:::caution `ui:` tags are public
A component's runtime data is served to anyone who loads your page, so treat every `ui:` tag as
public. Put nothing in a `ui:` tag that you would not show in your page source: no internal system
names, customer names or secrets. Use tags without the prefix for those; they stay private.
:::

## Adding a `ui:` tag

Add it like any other tag:

- **Dashboard:** the component's tag editor.
- **CLI:** `myop tags add ui:beta` (see [`myop tags`](/docs/cli/commands)).
- **MCP:** the `edit_component_tags` tool, e.g. `{ componentId, add: ["ui:beta"] }`.

Tags belong to the component, not to a version or an environment. You don't need to save a new
version or release one: the next page load picks up the change.

## Reading tags in the host app

Tags come with the component itself, in the same response the SDK already fetches to render it.
There is **no extra request**, and a component without `ui:` tags costs nothing extra.

### `getComponentTags(componentId, env?, preview?)`

This is a synchronous read from the SDK's cache. It returns `[]` when the component has no `ui:`
tags or hasn't been fetched yet. It never throws and never makes a request.

```typescript
import { preloadComponents, getComponentTags } from '@myop/sdk/helpers';

await preloadComponents(['my-component-id']);

if (getComponentTags('my-component-id').includes('ui:beta')) {
  showBetaBadge();
}
```

Pass the same `env` / `preview` you used to load the component if you load it with explicit
values. With no arguments it uses the same defaults as `preloadComponents`.

### From the cloud repository, before loading the component

If you fetch the config yourself, you can read the tags from it before you decide whether, or
how, to render the component:

```typescript
import { getCloudRepository, getComponentTags } from '@myop/sdk/helpers';
import { getHostModule } from '@myop/sdk';

// One request, the same one loadComponent would make, cached for this page
const config = await getCloudRepository().fetchComponentV2('my-component-id');

const tags = config.type.uiTags ?? [];   // ['ui:beta']
// or, equivalently: getComponentTags('my-component-id')

if (!tags.includes('ui:hidden')) {
  const { hostSDK } = await getHostModule();
  const component = await hostSDK.loadComponent(config, container); // no second request
}
```

`fetchComponentV2(componentId, env?, preview?)` takes the same `env` / `preview` arguments as
`getComponentTags`. When you create your own repository with `new CloudRepository(url)`, the same
two reads work on that instance: `repo.fetchComponentV2(id)` returns the config, and
`repo.getComponentTags(id)` returns its tags.

### `component.uiTags`

Every mounted component exposes its tags directly:

```typescript
import { getHostModule } from '@myop/sdk';

const { hostSDK } = await getHostModule();
const component = await hostSDK.loadComponent(componentConfig, container);

component.uiTags; // ['ui:beta']  (always an array, [] when there are none)
```

### Server-side rendering and hydration

If you resolve configs on a server and [hydrate](/docs/learnMyop/ReactHostSSR) them in the browser,
the tags travel inside the config. Nothing extra is needed: `getComponentTags` works on hydrated
components with no network round trip.

## Preview and released versions

Tags are the same in preview and in every environment, because they belong to the component and
not to a release. A component that is only in preview still returns its tags.

## Self-hosting

[Self-hosted exports](/docs/self-hosting) include each component's `ui:` tags, filtered the same
way. Re-export after changing tags so your static files pick up the change.

## Good uses, and what not to use them for

**Good uses:**
- **Feature flags and badges**: `ui:beta`, `ui:new`
- **Layout or theme hints**: `ui:layout-wide`, `ui:theme-dark`
- **Analytics labels**: `ui:campaign-q4`

**Not a fit:**
- **Access control.** Anyone can read the tags, so never use them to decide what a user is
  allowed to see.
- **Large or frequently changing data.** Use the component's data (`myop_init_interface`) for
  that.

## Requirements and limits

- `@myop/sdk` **0.3.37 or later**. Older SDKs ignore the field, so nothing breaks.
- Framework wrappers (`@myop/react`, `@myop/vue`, `@myop/angular`, `@myop/react-native`) don't
  expose tags yet. Use `@myop/sdk` directly for now.
- At most 50 `ui:` tags per component, each up to 100 characters. Duplicates are removed and the
  order you set is kept.
