> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userkit.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Componentes por script

> Login, conta, planos e cobrança numa página sem build — o SDK inteiro numa linha de HTML.

**É a forma mais curta de pôr o UserKit numa página**, e funciona em qualquer
stack: Rails, WordPress, Django, um HTML estático, uma landing page que alguém
edita num CMS. Não pede npm, não pede build, não pede React do seu lado.

```html theme={null}
<div data-userkit="UserButton"></div>

<script
  src="https://cdn.userkit.dev/userkit.js"
  data-publishable-key="uk_pk_live_…"
  defer
  crossorigin="anonymous"
></script>
```

O elemento diz **qual** componente; o script diz **de quem** é a página. Não há
mais nada a escrever.

Se o seu front-end já tem um build, o pacote `@userkit/react`
entrega os mesmos componentes como código — com tipos, SSR e composição. Os dois
desenham a mesma coisa; a diferença é o que você já tem montado.

<Note>
  **Se a sua página já carrega o `widget.js`, ela já monta estes componentes** — a
  mesma marcação, a mesma tag, sem uma segunda linha de `<script>`. O `userkit.js` é
  para quem quer os componentes e **nenhum** botão flutuante no canto: uma tela de
  login, uma página de preços. O que cada um baixa continua sendo só o que a página
  usa — o painel de ajuda não vem junto aqui, e o app dos componentes só é buscado na
  primeira montagem lá.

  Os dois na mesma página funcionam **em qualquer ordem**: o que rodar primeiro cria
  o cliente e o outro o adota — uma sessão, uma fila de eventos, uma contagem de
  páginas. Dê a **mesma `data-publishable-key`** às duas tags; uma página é um
  ambiente, e a tag que nomear uma segunda chave avisa no console e não faz nada.
</Note>

## Os nomes são os do pacote

`data-userkit="UserButton"` monta o que o `<UserButton />` renderiza, e os `data-*`
são as props dele. É de propósito: o dia em que você adotar um build, a migração é uma
reescrita de marcação em JSX com **os mesmos nomes** — nada de reaprender a API.

| `data-userkit`        | O que aparece                                                                                                                                                |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `UserButton`          | O avatar com o menu da conta                                                                                                                                 |
| `AccountSettings`     | Perfil, dispositivos, times                                                                                                                                  |
| `CustomerSwitcher`    | Trocar de time                                                                                                                                               |
| `CustomerProfile`     | Administrar o time atual                                                                                                                                     |
| `PricingTable`        | Os planos e preços do seu catálogo                                                                                                                           |
| `Billing`             | Assinatura, faturas e o portal do provedor                                                                                                                   |
| `Credits`             | O saldo de créditos da equipe, e o extrato                                                                                                                   |
| `Referrals`           | O código de indicação desta pessoa                                                                                                                           |
| `NotificationBell`    | O sino e a caixa dela. `data-variant="icon"` desenha só o ícone com um ponto, sem borda e sem contagem; `UserKit.notifications.show()` abre o painel de fora |
| `ChangelogBadge`      | As novidades não lidas                                                                                                                                       |
| `WhatsNew`            | O "enquanto você esteve fora", que se abre sozinho                                                                                                           |
| `FeedbackButton`      | O mural de ideias                                                                                                                                            |
| `OnboardingChecklist` | A trilha de primeiros passos                                                                                                                                 |
| `OnboardingLauncher`  | O botão inline que reabre um guia que a pessoa fechou                                                                                                        |
| `Survey`              | A pesquisa pendente, se houver                                                                                                                               |
| `Banner`              | O aviso publicado num slot — pede `data-slot`                                                                                                                |
| `HelpWidget`          | O painel de ajuda, aqui no fluxo da página                                                                                                                   |
| `SupportChat`         | O mesmo painel, pelo nome antigo dele                                                                                                                        |
| `Agreements`          | Os termos que esta pessoa ainda precisa aceitar                                                                                                              |
| `HelpMarkdown`        | Markdown como elementos — pede `data-source`                                                                                                                 |
| `SignedIn`            | Mostra o que estiver dentro só para quem tem sessão                                                                                                          |
| `SignedOut`           | O contrário — e nenhum dos dois desenha nada enquanto carrega                                                                                                |
| `SessionLoading`      | O esqueleto, enquanto a sessão está sendo restaurada                                                                                                         |
| `Verified`            | Só para uma sessão cuja identidade foi **provada**                                                                                                           |
| `Flag`                | Só enquanto uma flag estiver ligada — pede `data-name`                                                                                                       |

**Esta tabela é o pacote inteiro.** Todo componente que o `@userkit/react` exporta
está aqui, e um teste do repositório é o que mantém isso verdadeiro — o que existe com
build existe sem build, pelo mesmo nome.

## Os cinco que desenham a SUA marcação

`UserButton` recebe um elemento vazio e o preenche. Os cinco guardas são o oposto: o que
está dentro do elemento é justamente o que eles decidem mostrar.

```html theme={null}
<div data-userkit="SignedOut">
  <a href="/entrar">Entrar</a>
</div>

<div data-userkit="Flag" data-name="novo_checkout">
  <a href="/checkout-v2">Finalizar compra</a>
</div>
```

São os seus nós, não uma cópia deles: o script tira os filhos do elemento antes de
montar e devolve os mesmos nós dentro do guarda. Um `addEventListener` que a sua
página já tinha pendurado neles continua valendo.

Quando a resposta é não, eles **saem da página** em vez de ficarem escondidos — o que
importa, porque marcação escondida continua legível para quem abre o inspetor. E como
o `fallback` do `<Flag>` é marcação, e atributo não carrega marcação, a versão HTML
não tem esse prop: escreva o caminho antigo num segundo elemento com a flag ao
contrário, ou use `UserKit.mount()`, que aceita JavaScript de verdade.

## Props por atributo

`data-redirect-uri` vira `redirectUri`; `data-customer="false"` vira `customer: false`;
`data-poll-interval-ms="30000"` vira número. As três conversões são todas.

```html theme={null}
<div data-userkit="AccountSettings" data-customer="true" data-devices="false"></div>
<div data-userkit="Banner" data-slot="dashboard-top"></div>
<div data-userkit="Billing" data-portal-return-url="/conta"></div>
```

Uma data continua texto: `data-since="2026-01-01"` chega como string, não como
`2026`.

## Quando algo acontece

Um atributo não carrega função, então os retornos que uma página quer usar
chegam como evento de DOM, do próprio elemento:

```html theme={null}
<div id="usuario" data-userkit="UserButton"></div>
<script>
  document.getElementById("usuario").addEventListener("userkit:signedout", () => {
    location.href = "/";
  });
</script>
```

| Evento                  | De quem        | `detail`          |
| ----------------------- | -------------- | ----------------- |
| `userkit:signedout`     | `UserButton`   | `null`            |
| `userkit:priceselected` | `PricingTable` | `{ price, plan }` |

Os eventos sobem (`bubbles`), então um ouvinte no `document` também os pega.
Entrar não é um evento aqui porque não é algo que estes componentes fazem: o seu
próprio auth faz o login, e `UserKit.boot({ externalId, hash })` é a chamada
que diz à página quem é a pessoa.

## Quando o HTML não basta

Props que não cabem num atributo — uma lista, um objeto, uma função — vão pela API
global. Ela existe assim que o script carrega:

```html theme={null}
<script src="https://cdn.userkit.dev/userkit.js"
        data-publishable-key="uk_pk_live_…" data-auto="false" defer crossorigin="anonymous"></script>
<script>
  UserKit.mount("#precos", "PricingTable", {
    onSelected: (price, plan) => analytics.track("price_selected", { plan: plan.key }),
  });
</script>
```

| Chamada                             | O que faz                                                                                       |
| ----------------------------------- | ----------------------------------------------------------------------------------------------- |
| `UserKit.mount(alvo, nome, props?)` | Monta um componente. `alvo` é um seletor ou o elemento                                          |
| `handle.update(props)`              | Redesenha o componente montado com outros props, sem remontar                                   |
| `handle.unmount()`                  | Tira este componente da página, mesmo antes de ele ter aparecido                                |
| `UserKit.mountAll()`                | Monta todo `[data-userkit]` ainda não montado — útil depois de trocar o DOM                     |
| `UserKit.destroy()`                 | Tira todos da página                                                                            |
| `UserKit.boot(...)`                 | Identifica o visitante, igual ao SDK                                                            |
| `UserKit.ready()`                   | Promessa que resolve com o cliente, com a sessão já restaurada — ou `null` sem chave publicável |
| `UserKit.setTheme(...)`             | `"light"`, `"dark"` ou `"system"` para tudo que está montado                                    |
| `UserKit.client`                    | O cliente, para fazer o que os componentes não fazem                                            |

`data-auto="false"` desliga a montagem automática, para uma página que prefere
decidir a hora.

### O que o `mount()` devolve

Os componentes chegam da rede, então nada está na tela quando a chamada retorna.
O objeto devolvido responde às duas coisas que se quer disso: dá para **esperar**
por ele, e dá para **usá-lo antes**.

```js theme={null}
const banner = UserKit.mount("#aviso", "Banner", { slot: "dashboard-top" });

banner.update({ slot: "billing" });  // redesenha; não é uma segunda montagem
banner.unmount();                    // vale mesmo antes de o componente aparecer

const handle = await banner;         // o handle depois de estar na tela, ou null
```

`update()` **substitui** os props — não mistura com os anteriores — e é uma
re-renderização, não uma remontagem: um formulário meio preenchido sobrevive a
ela. É também como uma função passada por prop continua atual, porque os
handlers são lidos a cada desenho e não congelados no primeiro.

`unmount()` chamado antes de o pacote chegar cancela a montagem. Isso importa
para quem monta dentro de um framework: um componente que sai da tela enquanto o
pacote está na rede deixaria, sem isso, uma montagem presa num nó que o
framework já soltou.

<Note>
  Um elemento entregue ao `mount()` fica **reservado na hora**, então a varredura
  automática não o monta por cima com os `data-*` dele. Ainda assim, quem monta
  em código não precisa marcar o elemento com `data-userkit`: o nome já vai na
  chamada, e marcar os dois é pedir para as duas metades descreverem a mesma
  coisa.
</Note>

### Saber quando a tag carregou

A tag é `defer`, então a sua página não tem como saber quando o nosso arquivo
rodou. Os dois loaders avisam ao instalar `window.UserKit`. Verifique o objeto
**antes** de ouvir, e não há momento a perder:

```js theme={null}
function comecar(uk) { uk.mount("#aviso", "Banner") }

if (window.UserKit) comecar(window.UserKit);
else document.addEventListener("userkit:load", (e) => comecar(e.detail), { once: true });
```

`detail` é o próprio objeto. Numa página que carrega os **dois** arquivos o
evento dispara duas vezes: o `widget.js` alarga o objeto que o `userkit.js`
instalou, e a segunda vez é uma mudança real no que ele sabe fazer. `{ once: true }`
está certo para quem quer `mount`, que os dois têm, e errado para quem quer `show`.

## React sem colocar os componentes no bundle

Uma app React que carrega o script do CDN tem os dois lados: o React é dela, e os
componentes continuam vindo do CDN sob demanda. O pacote
[`@userkit/react-mount`](https://www.npmjs.com/package/@userkit/react-mount) é a
ponte — ele não desenha nada, e não importa nada em runtime além do React.

```bash theme={null}
npm i @userkit/react-mount
```

```tsx theme={null}
import { Banner, OnboardingChecklist } from "@userkit/react-mount";

<OnboardingChecklist props={{ slot: "dashboard", onStepAction: ir }} />
<Banner props={{ slot: "billing" }} />
```

Ele espera a tag sozinho, entrega props novas como re-renderização (não
remontagem), lê callbacks a cada render e cancela a montagem se o componente sair
da tela antes de o pacote chegar.

<Note>
  Se a sua app já empacota React e não se importa em empacotar os componentes
  também, o `@userkit/react` é mais direto —
  são componentes de verdade, com os props tipados. Este pacote é para quem quer
  especificamente que o desenho pesado continue no CDN.
</Note>

Os tipos do `window.UserKit` estão publicados em `@userkit/js/embed`, que não tem
metade em runtime — instale como `devDependency` e importe com `import type`:

```ts theme={null}
import type { UserKitEmbed } from "@userkit/js/embed";
```

### Montar o `@userkit/react` dentro de um shadow root

Se você renderiza os componentes do npm dentro de um shadow root seu, envolva
essa árvore num `<MountTargetProvider>`: assim a folha de estilo cai dentro do
limite e os flutuantes portam para dentro dele também — nenhum dos dois
atravessa um shadow boundary sozinho:

```tsx theme={null}
import { MountTargetProvider, UserKitProvider, UserButton } from "@userkit/react";

<MountTargetProvider value={{ styles: shadowRoot, portal: container }}>
  <UserKitProvider publishableKey="uk_pk_live_…">
    <UserButton />
  </UserKitProvider>
</MountTargetProvider>
```

Ele substitui o `setMountTarget({ styles, portal })`, que valia para a página
inteira: uma página pode ter duas árvores — o painel de ajuda no shadow root
dele e um drop-in no seu próprio layout — e uma configuração só fazia a última
montagem decidir pelas duas, então um componente montado depois de abrir o
painel desenhava sem folha de estilo e portava para dentro do limite do painel.
Passar `styles` ou `portal` para o `setMountTarget` hoje emite um aviso no
console e não faz nada; a função continua definindo o `nonce` do CSP, que é de
fato da página.

## Aparência

Os mesmos tokens do widget, no script:

```html theme={null}
<script src="https://cdn.userkit.dev/userkit.js"
        data-publishable-key="uk_pk_live_…"
        data-color-primary="#0b5cff"
        data-border-radius="0.75rem"
        data-theme="light"
        data-locale="en"
        defer crossorigin="anonymous"></script>
```

`data-theme` é `light`, `dark` ou `system` (o padrão), e é o que responde por uma
aplicação que decide o próprio tema: sem ele os componentes seguem o
`prefers-color-scheme` de quem está lendo, que discorda do interruptor no seu
cabeçalho na noite em que o sistema está escuro e a pessoa escolheu claro.

Aqui ele tem dois níveis, porque as perguntas são diferentes. No script vale para
a página; no elemento vale só para aquele componente, e **o do elemento vence** —
um `<PricingTable />` dentro de uma seção escura de uma página clara é uma decisão
de quem escreveu aquela div:

```html theme={null}
<div data-userkit="PricingTable" data-theme="dark"></div>
```

Com a página no ar, `UserKit.setTheme("dark")` (ou `"light"`, ou `"system"`)
redesenha todo componente montado — sem remontar, então um formulário meio
preenchido sobrevive à troca. Um elemento com `data-theme` próprio mantém o seu.

A tipografia **herda a da página** por padrão, e isso é deliberado: diferente do
widget flutuante, estes componentes não moram dentro de um shadow root. Eles são
colocados por você dentro do seu layout, então devem parecer parte dele — o seu CSS
alcança as classes `.uk-*`, que é exatamente o que o pacote npm oferece a quem tem
build.

## O que a página paga

| Arquivo              | Tamanho       | Quando                                                                            |
| -------------------- | ------------- | --------------------------------------------------------------------------------- |
| `userkit.js`         | \~11 KB gzip  | sempre                                                                            |
| `v/vendor-<hash>.js` | \~115 KB gzip | quando houver o primeiro ponto de montagem — React, o cliente e a folha de estilo |
| `u/app-<hash>.js`    | \~16 KB gzip  | junto — os componentes em si                                                      |

Uma página que carrega o script e não marca nada não baixa React. Os arquivos com
hash têm o hash do conteúdo no nome e cache de um ano; o primeiro nunca muda de
endereço e é revalidado dentro de uma hora, então uma correção chega à sua
página na navegação seguinte do visitante sem você publicar nada. O arquivo
compartilhado é o mesmo que o widget de ajuda usa, então uma página com os dois
baixa o React uma vez.

## Content Security Policy

```
script-src  'self' https://cdn.userkit.dev;
connect-src 'self' https://api.userkit.dev;
img-src     'self' https: data:;
```

Os componentes montam no seu próprio DOM, e a folha de estilo deles é movida
para uma folha construída (`document.adoptedStyleSheets`) no instante em que é
escrita, então `style-src` não precisa de nada por ela. Um navegador sem folhas
construídas, e a paleta escura definida no painel — que os componentes desenham
como um `<style>` na própria árvore — ainda pedem `style-src 'unsafe-inline'`;
sem isso os formulários desenham em claro.

O `crossorigin="anonymous"` na tag é o que faz um erro nosso chegar ao seu
`window.onerror` com stack em vez de como `"Script error."`.

## O que ele nunca faz

Não cria contato nenhum sozinho. `boot()` é uma decisão sua aqui como é no pacote —
um `<script>` que começasse a criar contatos com tráfego de landing page seria o
pior padrão possível.
