Como a Cal.com lançou uma app para iOS/Android usando Expo e Chrome/Firefox usando WXT numa única codebase
Sabe como a Peer e a equipa lançaram uma app nativa em menos de 3 semanas.

On this page
- Uma codebase. Cinco plataformas. Duas lojas de apps. Quatro navegadores.
- A ideia central: levar a Cal.com a todo o lado sem lançar a Cal.com cinco vezes
- iOS + Android: alimentadas pelo mais recente Expo e React Native
- Extensões: alimentadas por WXT
- A parte divertida: a UI da extensão do Chrome alimentada por Expo Web
- Menos de 3 semanas… porque a base estava certa
- Porque isto importa (além de “olha o que construímos”)
- Créditos a quem os devemos
- O que vem a seguir
Uma codebase. Cinco plataformas. Duas lojas de apps. Quatro navegadores.
Lançado em menos de três semanas.
A Cal.com sempre quis aproximar-se das pessoas onde elas já estão.
Para agendamento, isso quer dizer em todo o lado: no teu telemóvel, no teu navegador, no separador que nunca fechas, no portátil que usas para trabalhar e no dispositivo que agarras quando estás a sair a correr.
Muita gente pediu-nos isto e, por fim, decidimos experimentar. Esta semana anunciámos:
-
Uma app nativa para iOS
-
Uma app nativa 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 isto ao longo de meses ou trimestres.
Lançámos tudo isto em menos de três semanas, da ideia à produção.
Tudo sem dividir o produto em uma dúzia de repositórios que se afastam lentamente com o tempo.
Fizemo-lo com uma única codebase.
A ideia central: levar a Cal.com a todo o lado sem lançar a Cal.com cinco vezes
Historicamente, “app mobile” e “extensão de navegador” vivem em universos diferentes.
-
Mobile quer React Native, tooling de build nativo, fluxos da app store, notificações push, deep linking, etc.
-
As extensões querem content scripts, background/service workers, permissões, APIs de armazenamento, particularidades dos navegadores e o jogo interminável de “funciona no Chrome, mas…”.
A abordagem habitual é aceitar a divisão:
-
equipas separadas
-
stacks separadas
-
processos de lançamento separados
-
bugs separados
-
inconsistências de UI separadas
Isso não parecia a forma da Cal.com.
Queríamos que a Cal tivesse a mesma experiência em todo o lado. A mesma UI. O mesmo modelo mental. As mesmas funcionalidades. O mesmo ritmo de iteração.
Por isso construímo-la assim.
iOS + Android: alimentadas pelo mais recente Expo e React Native
As nossas apps para iOS e Android são alimentadas pelo mais recente Expo (expo.dev) e React Native (RN).
O Expo chegou a um ponto em que consegues o melhor dos dois mundos:
-
excelente experiência de desenvolvimento
-
preparação séria para produção
-
flexibilidade quando precisas de ir nativo
Mais importante ainda, o Expo encaixa numa tese maior a que damos muita importância:
Web e nativo não deviam parecer produtos separados.
Deviam parecer superfícies diferentes do mesmo sistema.
Extensões: alimentadas por WXT
Para extensões de navegador, construímos sobre wxt.dev.
As extensões continuam a ser uma daquelas áreas em que o tooling parece estranhamente preso no passado. O WXT faz com que pareça moderno:
-
estrutura limpa
-
defaults sensatos
-
builds fiáveis para vários navegadores
E sim, construímos para Chrome, Firefox, Safari e Edge.
Só o Safari normalmente já é suficiente para afastar equipas do suporte multi-navegador. Mas o objetivo nunca foi “Chrome agora, os outros depois”.
O objetivo era Cal em todo o lado.
A parte divertida: a UI da extensão do Chrome alimentada por Expo Web
Aqui está a parte que ainda parece um pouco surreal:
A UI da nossa extensão do Chrome é alimentada por Expo Web.
Isso quer dizer que a UI da extensão não é um frontend separado com o seu próprio sistema de componentes e deriva de estilo. É construída usando a mesma abordagem de UI que já usamos para mobile a correr na web dentro da superfície da extensão.
Por isso, quando digo uma única codebase, estou a falar de algo que realmente importa:
-
ecrãs partilhados
-
componentes partilhados
-
primitives de design partilhadas
-
lógica partilhada
-
velocidade partilhada
A própria app WXT corre dentro de /extension/entrypoints/content.ts, que depois carrega a app expo-web como um iframe. Esta é a abordagem mais comum para construir extensões "sidebar" do Chrome e aprendi este truque com Wilson Wilson (@euboid). Infelizmente, o popup oficial da extensão do Chrome tem uma altura máxima de 600px.
Até espalhámos algumas APIs só do navegador, como injetar o nosso widget da Cal.com em mail.google.com e calendar.google.com, o que pode ser feito facilmente na pasta /extensions a partir do WXT.
Pela minha experiência, esta combinação: iOS + Android nativos via Expo, extensões multi-browser via WXT, UI da extensão via Expo Web, tudo ligado numa única codebase coerente é algo que, pessoalmente, nunca vi ser feito assim antes.
Talvez exista algures. A internet é grande.
Mas eu não vi, e definitivamente não havia um playbook que pudéssemos seguir.
Por isso criámos o nosso.
Menos de 3 semanas… porque a base estava certa
Lançar isto tão depressa não teve a ver com heroísmo ou com horas a mais.
Teve a ver com ter a base certa.
Tudo depende da mais recente e melhor API v2 da Cal.com, que usa OAuth para autorização. Isso significou:
-
fluxos de autenticação limpos
-
acesso seguro
-
um modelo consistente entre apps e extensões
-
sem truques, sem atalhos
A mesma API alimenta mobile, web e extensões. Sem “endpoints só para a extensão”. Sem “exceções para mobile”.
Só uma API, desenhada para escalar com o ecossistema.
👉 Se estás a construir apps ou integrações em cima da Cal.com, este é o teu ponto de partida:
https://cal.klipni.com/help/apps-and-integrations/oauth
Essa base de OAuth é o que torna realista lançar novas apps de agendamento tão depressa sem comprometer a segurança ou a sanidade dos developers.
Porque isto importa (além de “olha o que construímos”)
Le envio para muitas plataformas só impressiona 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 a todo o lado, não é “mobile first, extension later”
-
Velocidade: um único sítio para construir, um único sítio para corrigir
-
Qualidade: menos divergências específicas de cada plataforma
-
Momento: a equipa constrói produto em vez de código de cola
As extensões permitem-nos viver diretamente dentro dos workflows. O mobile permite-nos viver no mundo real. Juntas, essa área de superfície torna-se uma vantagem do produto.
Créditos a quem os devemos
Isto não aconteceu por causa de uma só pessoa. Mas duas pessoas foram multiplicadores enormes:
-
Wilson Wilson (@euboid), uma enorme quantidade do suporte da extensão de browser, casos-limite entre browsers e o conhecimento de “é este o truque que realmente funciona” veio de Wilson.
-
Beto (@betomoedano), imensa da experiência com Expo e ajuda prática que transforma “isto devia funcionar” em “fica bom, sente-se bem” veio de Beto.
-
e a equipa trabalhadora da call, nomeadamente: eu próprio, Dhairyashil Shinde, Carina Wollendorfer e
Volnei Munhoz e Claude Code.
Obrigado a todos. A sério.
O que vem a seguir
Curiosidade: As nossas apps ainda não foram lançadas, o que é até um bocado hilariante. O processo de inscrição na App Store está a demorar mais do que o próprio desenvolvimento da app. Por alguma razão, ainda há problemas de KYC com a nossa empresa (mudámos de nome há alguns anos, incompatibilidade do número D-U-N-S, um funcionário não-americano a controlar uma empresa dos EUA, bla bla bla.)

Às vezes gostava que o desenvolvimento de apps mobile pudesse ter o mesmo ritmo que estamos habituados a ter na web, mas isso é muito improvável.
De qualquer forma, pleeease inscreve-te na nossa waitlist em cal.com/app e recebe uma notificação assim que as apps de produção forem lançadas nas lojas. Segundo a equipa de Enrolment, faltam só mais alguns dias, mas as férias estão a chegar, por isso nunca se sabe.
A codebase ainda está um bocado desarrumada, com componentes grandes, mas estamos a limpá-los agora.
No fim de contas, a parte mais entusiasmante é conseguir “Cal.com em todo o lado” com tão pouco overhead de engenharia para manter.
É que agora temos uma base em que lançar a próxima coisa parece como deve ser:
-
constrói uma vez
-
lança em todo o lado
-
itera depressa
Esse é o nível.
E é um nível bastante divertido de ultrapassar.
