Blog

Como a Cal.com lançou um app para iOS/Android usando Expo e Chrome/Firefox usando WXT em uma única codebase

Saiba como a Peer e a equipe lançaram um app nativo em menos de 3 semanas.

Peer RichelsenDec 17, 2025

Uma codebase. Cinco plataformas. Duas lojas de aplicativos. Quatro navegadores.

Lançado em menos de três semanas.

A Cal.com sempre foi sobre encontrar as pessoas onde elas já estão.

Para agendamento, isso significa em todo lugar: no seu celular, no seu navegador, na aba que você nunca fecha, no laptop que você usa para trabalhar e no dispositivo que você pega quando está saindo correndo.

Muita gente pediu por isso e finalmente decidimos testar. Nesta semana, anunciamos:

  • Um app nativo para iOS

  • Um app nativo para Android

  • Uma extensão do Chrome

  • Uma extensão do Firefox

  • Uma extensão do Safari

  • Uma extensão do Edge

E não fizemos isso ao longo de meses ou trimestres.

Lançamos tudo isso em menos de três semanas, da ideia à produção.

Tudo sem dividir o produto em uma dúzia de repositórios que vão se afastando aos poucos com o tempo.

Fizemos isso com uma codebase.

A ideia central: levar a Cal.com a todo lugar sem lançar a Cal.com cinco vezes

Historicamente, “app móvel” e “extensão de navegador” vivem em universos diferentes.

  • Mobile quer React Native, ferramentas nativas de build, fluxos da app store, notificações push, deep linking etc.

  • Extensões querem content scripts, background/service workers, permissões, APIs de armazenamento, peculiaridades dos navegadores e o jogo sem fim do “funciona no Chrome, mas…”.

A abordagem habitual é aceitar a divisão:

  • equipes separadas

  • stacks separadas

  • processos de lançamento separados

  • bugs separados

  • inconsistências de UI separadas

Isso não parecia o jeito da Cal.com.

Queríamos que a Cal tivesse a sensação de o mesmo produto em todo lugar. Mesma UI. Mesmo modelo mental. Mesmas funcionalidades. Mesma velocidade de iteração.

Então construímos assim.

iOS + Android: impulsionados pelo Expo e React Native mais recentes

Nossos apps para iOS e Android são impulsionados pelo Expo mais recente (expo.dev) e React Native (RN).

O Expo chegou a um ponto em que você tem o melhor dos dois mundos:

  • excelente experiência de desenvolvimento

  • prontidão séria para produção

  • flexibilidade quando você precisa ir para o nativo

E, o mais importante, o Expo se encaixa em uma tese maior com a qual nos importamos muito:

Web e nativo não deveriam parecer produtos separados.

Eles deveriam parecer superfícies diferentes do mesmo sistema.

Extensões: impulsionadas por WXT

Para extensões de navegador, construímos sobre o wxt.dev.

Extensões ainda são uma daquelas áreas em que as ferramentas parecem estranhamente presas no passado. O WXT faz tudo parecer moderno:

  • estrutura limpa

  • padrões sensatos

  • builds confiáveis para múltiplos navegadores

E sim, construímos para Chrome, Firefox, Safari e Edge.

Só o Safari normalmente já é suficiente para afastar equipes do suporte a múltiplos navegadores. Mas o objetivo nunca foi “Chrome agora, os outros depois”.

O objetivo era Cal em todo lugar.

A parte divertida: UI da extensão do Chrome impulsionada por Expo Web

Aqui está a parte que ainda parece um pouco surreal:

Nossa UI da extensão do Chrome é impulsionada por Expo Web.

Isso significa que a UI da extensão não é um frontend separado, com seu próprio sistema de componentes e divergência de estilo. Ela é construída usando a mesma abordagem de UI que já usamos para mobile, rodando na web dentro da superfície da extensão.

Então, quando eu digo uma codebase, eu quero dizer isso de um jeito que realmente importa:

  • telas compartilhadas

  • componentes compartilhados

  • primitives de design compartilhadas

  • lógica compartilhada

  • velocidade compartilhada

O próprio app do WXT roda dentro de /extension/entrypoints/content.ts, que então carrega o app expo-web como um iframe. Essa é a abordagem mais comum para construir extensões do Chrome em formato de “sidebar”, e aprendi esse truque com Wilson Wilson (@euboid). Infelizmente, o “popup” oficial da extensão do Chrome tem uma altura máxima de 600px.

Também inserimos algumas APIs exclusivas do navegador, como injetar nosso widget da Cal.com em mail.google.com e calendar.google.com, o que pode ser feito facilmente na pasta /extensions do WXT.

Pela minha experiência, essa combinação: iOS nativo + Android via Expo, extensões para múltiplos navegadores via WXT, UI da extensão via Expo Web, tudo conectado em uma única codebase coerente é algo que, pessoalmente, eu não tinha visto ser feito assim antes.

Talvez exista em algum lugar. A internet é enorme.

Mas eu não vi, e com certeza não havia um playbook que pudéssemos seguir.

Então criamos o nosso.

Menos de 3 semanas… porque a base estava certa

Lançar tão rápido não teve nada a ver com heroísmo ou esforço extremo.

Teve a ver com ter a base certa.

Tudo depende da API v2 da Cal.com, a mais recente e avançada, que usa OAuth para autorização. Isso significou:

  • fluxos de autenticação limpos

  • acesso seguro

  • um modelo consistente entre apps e extensões

  • sem gambiarras, sem atalhos

A mesma API impulsiona mobile, web e extensões. Sem “endpoints só para extensão”. Sem “exceções para mobile”.

Apenas uma API, projetada para escalar com o ecossistema.

👉 Se você estiver construindo apps ou integrações em cima da Cal.com, este é o seu ponto de partida:
https://cal.klipni.com/help/apps-and-integrations/oauth

Essa base de OAuth é o que torna realista lançar novos apps de agendamento tão rápido sem comprometer a segurança nem a sanidade do desenvolvedor.

Por que isso importa (além de “olha o que construímos”)

Distribuir para muitas plataformas só é impressionante se continuar fácil de manter.

A verdadeira vitória não é a lista de plataformas. A verdadeira vitória é:

  • Consistência: as funcionalidades chegam em todo lugar, não “mobile first, extensão depois”

  • Velocidade: um lugar para construir, um lugar para corrigir

  • Qualidade: menos divergências específicas de cada plataforma

  • Impulso: o time constrói produto em vez de código cola

As extensões nos permitem viver diretamente dentro dos workflows. O mobile nos permite viver no mundo real. Juntos, essa superfície vira uma vantagem de produto.

Créditos a quem merece

Isso não aconteceu por causa de uma única pessoa. Mas duas pessoas foram multiplicadores enormes:

  • Wilson Wilson (@euboid), uma quantidade enorme do suporte à extensão do navegador, dos casos extremos entre navegadores e do conhecimento de “é esse o truque que realmente funciona” veio do Wilson.

  • Beto (@betomoedano), muita da experiência com Expo e da ajuda prática que transforma “isso deveria funcionar” em “ficou bom, parece bom” veio do Beto.

  • e a equipe dedicada da call, ou seja: eu, Dhairyashil Shinde, Carina Wollendorfer e

    Volnei Munhoz e Claude Code.

Obrigado a todos. Sério.

O que vem a seguir

Curiosidade: nossos apps ainda não foram lançados, o que é meio hilário. O processo de entrada na App Store está levando mais tempo do que o desenvolvimento inteiro do app. Por algum motivo ainda existem problemas de KYC com a nossa empresa (mudamos o nome há alguns anos, divergência no número D-U-N-S, funcionário fora dos EUA controlando uma empresa dos EUA, blá blá blá.)

Screenshot of an Apple developer support email

Às vezes eu queria que o desenvolvimento de app mobile pudesse acompanhar o mesmo ritmo que estamos acostumados a ter na web, mas isso é muito improvável.

Enfim, pleeease se inscreva na nossa lista de espera em cal.com/app e seja notificado assim que os apps de produção entrarem nas lojas. Segundo a equipe de Enrolment, faltam só mais alguns dias, mas as festas de fim de ano estão chegando, então nunca se sabe.

A base de código ainda está meio bagunçada, com componentes grandes, mas estamos limpando isso agora.

No fim, a parte mais empolgante é chegar a “Cal.com em todo lugar” com um esforço de engenharia tão baixo para manter.

É que agora temos uma base em que lançar a próxima coisa parece do jeito que deveria ser:

  • construir uma vez

  • lançar em todo lugar

  • iterar rápido

Esse é o nível.

E é um nível bem divertido de alcançar.

Peer Richelsen

Post by

Peer Richelsen

Co-Founder