Skip to main content
É 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.
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.
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.

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. 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.
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.
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:
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:
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.
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.
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.

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:
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 é a ponte — ele não desenha nada, e não importa nada em runtime além do React.
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.
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.
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:

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:
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:
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:
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

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

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.