{"id":216,"date":"2024-11-25T16:58:43","date_gmt":"2024-11-25T19:58:43","guid":{"rendered":"https:\/\/alice.com.br\/tech\/?p=216"},"modified":"2026-01-19T22:04:17","modified_gmt":"2026-01-20T01:04:17","slug":"os-desafios-de-migrar-para-uma-stack-de-next-js-stencil-e-ssr","status":"publish","type":"post","link":"https:\/\/alice.com.br\/tech\/os-desafios-de-migrar-para-uma-stack-de-next-js-stencil-e-ssr\/","title":{"rendered":"Migrar para Next.js e SSR: desafios e aprendizados"},"content":{"rendered":"<p>O site da Alice, assim como o da maior parte das empresas de tecnologia hoje, nasceu como uma p\u00e1gina est\u00e1tica. Mas, ao longo do tempo, incorporou uma gama de funcionalidades que refletem nossos diferenciais como healthtech. Viabilizar essa transi\u00e7\u00e3o, no entanto, exigiu migrar toda sua base de c\u00f3digo para um novo framework, um projeto \u00e1rduo que gerou melhora substancial na experi\u00eancia do usu\u00e1rio, na performance do site e reduziu os custos de manuten\u00e7\u00e3o.<\/p>\n<p>Hoje, o projeto faz uso do Next.js, num contexto de Server-Side Rendering (SSR) e um design system baseado em Stencil. Essa migra\u00e7\u00e3o, por\u00e9m, gerou desafios t\u00e9cnicos importantes que exigiram mudan\u00e7as mais amplas em nossa arquitetura.<\/p>\n<p>Neste artigo, exploramos os problemas e desafios que enfrentamos ao adotar essa stack, com foco em &#8220;piscas&#8221; na interface com o usu\u00e1rio final, e como os solucionamos e garantimos a melhor experi\u00eancia e funcionalidades para quem est\u00e1 na ponta.<\/p>\n<h2>Next.js, Stencil e SSR<\/h2>\n<p>Quando criamos o site adotamos o Nuxt 2 como framework. Foi a escolha apropriada para o momento da empresa e as op\u00e7\u00f5es dispon\u00edveis \u00e0 \u00e9poca. \u00c0 medida que a Alice se desenvolveu, por\u00e9m, o projeto foi crescendo em complexidade. Em poucos anos, implementamos subprodutos no site como uma <a href=\"https:\/\/alice.com.br\/calculadora-de-saude\">calculadora de sa\u00fade<\/a>, um <a href=\"https:\/\/www.alice.com.br\/simulador\">simulador de planos<\/a> e um sistema de consulta \u00e0 nossa <a href=\"https:\/\/alice.com.br\/rede-credenciada\">rede credenciada<\/a>.<\/p>\n<p>Migrar para o Nuxt 3 se provou uma op\u00e7\u00e3o indevida \u2013 tratava-se de um framework ainda imaturo que, por caracter\u00edsticas de nossa base de c\u00f3digo, exigiria tanto esfor\u00e7o quanto o necess\u00e1rio para migrar para outro framework. Em vez disso, optamos pelo Next.js.<\/p>\n<p>Qualquer migra\u00e7\u00e3o, por\u00e9m, traz desafios. No nosso caso, precis\u00e1vamos integrar perfeitamente uma aplica\u00e7\u00e3o em Next.js em SSR com o design system da Alice, baseado em Stencil.js, que exporta <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_components\">web components<\/a> que podem ser encapsulados para React.<\/p>\n<p>Imediatamente, enfrentamos problemas: um constante &#8220;pisca&#8221; na tela, que afetava negativamente a experi\u00eancia do usu\u00e1rio, prejudicava as m\u00e9tricas do Core Web Vitals e reduzia a performance de nossas p\u00e1ginas em buscas no Google.<\/p>\n<p>Uma investiga\u00e7\u00e3o revelou que esse problema, conhecido como &#8220;flash of unstyled content&#8221; (FOUC), resultava da intera\u00e7\u00e3o entre nosso design system, em Stencil.js, com projetos em SSR, como o Next.js. Mais especificamente, tratava-se de problemas na etapa de hidrata\u00e7\u00e3o da p\u00e1gina.<\/p>\n<h2>Hidrata\u00e7\u00e3o<\/h2>\n<p>Vamos comparar a constru\u00e7\u00e3o de uma p\u00e1gina web com a montagem de uma casa. Inicialmente, voc\u00ea organiza a casa com m\u00f3veis essenciais e decora\u00e7\u00e3o, preparando-a para receber visitas. Esse processo se assemelha \u00e0 pr\u00e9-renderiza\u00e7\u00e3o de uma p\u00e1gina no Next.js, onde a estrutura b\u00e1sica \u00e9 montada no lado servidor antes de qualquer intera\u00e7\u00e3o do usu\u00e1rio.<\/p>\n<p>Quando algu\u00e9m visita a casa, n\u00e3o se limita a observar os m\u00f3veis; interage com o ambiente, abre portas ou acende luzes. Na web, essa intera\u00e7\u00e3o \u00e9 poss\u00edvel gra\u00e7as \u00e0 &#8220;hidrata\u00e7\u00e3o&#8221;. Ap\u00f3s a p\u00e1gina ser carregada, os elementos se tornam interativos, permitindo ao usu\u00e1rio realizar a\u00e7\u00f5es como cliques e toques.<\/p>\n<h2>Como era em uma stack baseada em Nuxt 2<\/h2>\n<p>Numa stack baseada em Nuxt 2 j\u00e1 consegu\u00edamos utilizar os Web components do design system encapsulados para Vue, mas eles eram considerados como client components. Ou seja, toda a hidrata\u00e7\u00e3o e constru\u00e7\u00e3o da p\u00e1gina era feita do lado do cliente\/navegador, prejudicando assim o tempo total de carregamento e, consequentemente, as notas do Core Web Vitals do site como um todo.<\/p>\n<p>O projeto j\u00e1 tinha outros problemas relacionados a performance e experi\u00eancia de desenvolvimento, pois era muito desafiador escalar p\u00e1ginas mantendo uma boa performance combinado com uma boa experi\u00eancia de codifica\u00e7\u00e3o e implementa\u00e7\u00e3o de testes (unit\u00e1rios e e2e).<\/p>\n<p>Um dos pontos que identificamos como defasado no projeto era a vers\u00e3o do Vue utilizada pelo Nuxt 2, pois o framework estava travado na vers\u00e3o 2.6.14 do Vue, que j\u00e1 era considerada legado. Isso nos impedia de atualizar em rela\u00e7\u00e3o a outras solu\u00e7\u00f5es, como por exemplo o testing-library, cypress, design system, storybook, node, etc.<\/p>\n<p>Al\u00e9m disso, identificamos tamb\u00e9m outros problemas de experi\u00eancia de desenvolvimento como auto importa\u00e7\u00e3o de arquivos em testes, mock de endpoints e store, tempo elevado de execu\u00e7\u00e3o de build, test e lint.<\/p>\n<h2>Como a p\u00e1gina operava no Next.js<\/h2>\n<p>No entanto, no Next.js, apesar de n\u00e3o passarmos pelos problemas citados acima, ainda precis\u00e1vamos resolver a quest\u00e3o da hidrata\u00e7\u00e3o dos componentes do Design System com o Next server.<\/p>\n<p>Tecnicamente, a hidrata\u00e7\u00e3o vincula o HTML pr\u00e9-renderizado ao JavaScript necess\u00e1rio para tornar a p\u00e1gina interativa. Ent\u00e3o, quando o navegador carrega a p\u00e1gina, o JavaScript \u00e9 executado, permitindo intera\u00e7\u00f5es \u2013 e esse processo \u00e9 conhecido como hidrata\u00e7\u00e3o. Ele inicializa a aplica\u00e7\u00e3o no lado do cliente ap\u00f3s o carregamento, reativando event handlers e estados previamente definidos no HTML est\u00e1tico.<\/p>\n<p>Em um aplicativo Next.js, a p\u00e1gina \u00e9 parcialmente pr\u00e9-renderizada no servidor e entregue como HTML est\u00e1tico. Em seguida, o JavaScript necess\u00e1rio para sua interatividade \u00e9 enviado e executado pelo navegador. Durante essa execu\u00e7\u00e3o, ocorre a hidrata\u00e7\u00e3o, onde o React reconecta os manipuladores de eventos e estados \u00e0 estrutura de componentes j\u00e1 renderizada. Esse intervalo de tempo foi o que causou o &#8220;pisca&#8221; em nossa p\u00e1gina.<\/p>\n<p>Embora o Stencil j\u00e1 esteja implementando suporte dos web components para SSR, n\u00e3o poder\u00edamos esperar o lan\u00e7amento dessa funcionalidade, ent\u00e3o por meio de uma s\u00e9rie de estudos e benchmarking, conseguimos encontrar uma solu\u00e7\u00e3o entre SSR e client web components.<\/p>\n<p>A ideia principal era garantir que os web components do design system chegassem no cliente final totalmente hidratados e, para isso, criamos um servidor customizado em node e substitu\u00edmos pelo Next server padr\u00e3o onde combinamos recursos de renderiza\u00e7\u00e3o do Next com a hidrata\u00e7\u00e3o do Stencil nos componentes do design system antes mesmo da resposta chegar no navegador.<\/p>\n<p>Essa camada funciona especificamente para p\u00e1ginas com conte\u00fados totalmente est\u00e1ticos, ent\u00e3o tamb\u00e9m precis\u00e1vamos encontrar uma solu\u00e7\u00e3o para p\u00e1ginas din\u00e2micas. Para isso, usamos um conceito de Provider, que se trata da inje\u00e7\u00e3o de um componente em React respons\u00e1vel por hidratar os componentes do design system. Tudo isso foi feito de forma transparente para o usu\u00e1rio final, justamente na camada de Layout, ou seja, uma camada que age logo depois da resposta do servidor no navegador e antes do usu\u00e1rio receber a p\u00e1gina totalmente renderizada.<\/p>\n<p>Observa\u00e7\u00e3o importante: A proposta final do Stencil \u00e9 usarmos os web components como server components. Esse suporte foi disponibilizado recentemente e estamos realizando uma s\u00e9rie de testes para consolidar de vez o Stencil + Nextjs + SSR.<\/p>\n<h2>Resultados<\/h2>\n<p>Os resultados foram observados imediatamente. Nossos Core Web Vitals subiram de 72 para 90 na compara\u00e7\u00e3o com a implementa\u00e7\u00e3o em Nuxt 2, com os escores de SEO saltando de 61 para 100.<\/p>\n<p>Em m\u00e9dia, o tempo de carregamento foi reduzido em 50%.<\/p>\n<h2>A log\u00edstica da migra\u00e7\u00e3o<\/h2>\n<p>Encontrar a solu\u00e7\u00e3o t\u00e9cnica \u00e9, por\u00e9m, apenas metade do caminho. S\u00f3 conseguir\u00edamos extrair valor dessa iniciativa quando implement\u00e1ssemos a nova stack em toda nossa base de c\u00f3digo, um processo trabalhoso e arriscado.<\/p>\n<p>Dois times se dedicaram a realizar a migra\u00e7\u00e3o: o squad de engenharia de produto de vendas, que \u00e9 formalmente respons\u00e1vel pelo site, al\u00e9m de uma s\u00e9rie de outros sistemas e entregas; e o time de plataforma em engenharia.<\/p>\n<p>A primeira grande decis\u00e3o a ser tomada era entre migrar de uma s\u00f3 vez ou reescrever gradualmente o projeto. Ambas as abordagens exigiriam esfor\u00e7o significativo. No entanto, a reescrita gradual permitiria que realiz\u00e1ssemos em paralelo a transi\u00e7\u00e3o para o Next.js com as entregas regulares do squad de vendas. Al\u00e9m disso, poder\u00edamos monitorar a performance e os resultados da migra\u00e7\u00e3o de cada funcionalidade, gerando aprendizados cont\u00ednuos e refinamentos a cada p\u00e1gina.<\/p>\n<p>Optamos, ent\u00e3o, por reescrever gradualmente todo o projeto, come\u00e7ando pela p\u00e1gina inicial, avaliando m\u00e9tricas relevantes como convers\u00e3o e performance por meio de testes A\/B. O resultado foi uma implementa\u00e7\u00e3o suave, acelerada e bem-sucedida, demarcando o modelo para migra\u00e7\u00f5es semelhantes no futuro.<\/p>\n\n<h2><b>____________________________________________________________________<\/b><\/h2>\n<h2>The challenges of migrating to a Next.js, Stencil and SSR stack<\/h2>\n<p><i>How Alice&#8217;s engineering teams implemented a new technology stack for our website, improving load times by 50% and enhancing user experience.<\/i><\/p>\n<p>Alice&#8217;s website, like many technology companies today, started as a static page. As we evolved, we integrated several functionalities that highlight our unique position as a healthtech company. To support this transition, we migrated the entire codebase to a new framework, a taxing project that yielded substantial improvements in user experience, website performance, and slashed maintenance costs.<\/p>\n<p>Our current tech stack is based on Next.js with Server-Side Rendering (SSR) and a design system powered by Stencil. This migration involved significant technical challenges that necessitated broader architectural changes.<\/p>\n<p>In this article, we delve into the specific problems and challenges we encountered while adopting this stack, with a particular focus on addressing &#8220;flashes&#8221; in the interface. We&#8217;ll discuss how we overcame these issues to ensure the best possible experience and functionality for our end-users.<\/p>\n<h3>Next.js, Stencil and SSR<\/h3>\n<p>When we started the website, we adopted the Nuxt 2 framework. It was a suitable choice given the company&#8217;s stage and the available options at the time. As Alice evolved, however, the project&#8217;s complexity increased. Within a few years, we implemented subproducts into the website including a <a href=\"https:\/\/alice.com.br\/calculadora-de-saude\">health calculator<\/a>, a <a href=\"https:\/\/www.alice.com.br\/simulador\">plan simulator<\/a> and a system allowing users to browse our accredited network of <a href=\"https:\/\/alice.com.br\/rede-credenciada\">health community<\/a>.<\/p>\n<p>Migrating to Nuxt 3 proved insufficient &#8211; it was a framework still in its early stages, and due to specificities in our codebase, it would require as much effort to implement as a migration to another framework. Instead, we opted for Next.js.<\/p>\n<p>Any migration, however, bears challenges. In our case, we needed to perfectly integrate an application based on Next.js in SSR with Alice&#8217;s design system, oriented around Stencil.js, that exports web components that can be encapsulated for React.<\/p>\n<p>We immediately faced a significant challenge: a constant screen flashing, which negatively impacted user experience, depressed Core Web Vitals, and reduced the website&#8217;s performance in Google search results.<\/p>\n<p>An investigation revealed that this issue, known as \u2018Flash of Unstyled Content\u2019 (FOUC), was a consequence of the interaction between our Stencil.js design system and SSR projects such as Next.js. More specifically, it was a problem during the page&#8217;s hydration step.<\/p>\n<h3>Hydration<\/h3>\n<p>Creating a webpage is somewhat like setting up your home. First, you organize your living room with essential furniture and decoration, preparing it to host visits. This process is similar to prerendering a Next.js page, where its basic structure is built on the server before any user interaction.<\/p>\n<p>When someone visits that apartment, they don&#8217;t just look at the furniture; they interact with their surroundings, turning on lights, opening doors, and so on. On the web, these interactions are made possible by &#8216;hydration&#8217;. Once the page loads, all elements become interactive, allowing users to click, tap, and more.<\/p>\n<h3>Using a Nuxt 2 stack<\/h3>\n<p>In a Nuxt2 stack, we were able to utilize web components from the design system encapsulated for Vue, but they were considered client components. The whole hydration and construction of the web page happened at the client side, hampering overall load times and, consequently, the whole website&#8217;s Core Web Vitals.<\/p>\n<p>We had already identified other problems related to performance and development experience. It was very challenging to scale up pages maintaining good performance, an effective coding experience and implementing e2e and unit tests.<\/p>\n<p>One of the main improvement points was the version of Vue used by Nuxt 2: we were still stuck with Vue&#8217;s legacy 2.6.14 version. That prevented us from updating other solutions, such as testing-library, cypress, design system, storybook, node, etc.<\/p>\n<p>We also identified other developer experience problems such as auto-importing of test files, endpoint mocks and store, and overly long times for executing build, test, and lint.<\/p>\n<h3>How the page operated under Next.js<\/h3>\n<p>Under Next.js, though we didn&#8217;t encounter the same issues, we still needed to solve the problems involved in hydrating the design system components with the Next server.<\/p>\n<p>Technically, the hydrating step connects a pre-rendered HTML with the JavaScript needed to make the page interactive. In other words, when the browser loads the webpage, it executes the JavaScript, allowing for interactions \u2013 and this process is known as hydration. It starts the application in the client side after loading, reactivating event handlers and states previously defined in the static HTML.<\/p>\n<p>In a Next.js application, the page is partially pre-rendered on the server and delivered as static HTML. Subsequently, the necessary JavaScript for interactivity is sent and executed by the browser. During this execution, hydration occurs, where React reconnects event handlers and state to the already rendered component structure. This time interval caused the flash on our page.<\/p>\n<p>Although Stencil was developing SSR support for web components, we couldn&#8217;t wait for the launch. Through research and benchmarking, we found a solution between SSR and client web components.<\/p>\n<p>Our primary goal was to ensure fully hydrated design system components on the client side. To make it happen, we created a custom Node.js server to replace the standard Next server, combining Next&#8217;s rendering resources with Stencil&#8217;s hydration in the design system components before the response reached the browser.<\/p>\n<p>That layer worked well for fully static pages, but we also needed to find a solution for dynamic pages. So, we implemented a Provider concept, injecting a React component responsible for hydrating design system components. All that happened in the Layout layer, that is, a layer activated as soon as the server responds to the browser and before the end user receives the fully rendered webpage.<\/p>\n<p>It&#8217;s worth noting that Stencil&#8217;s final proposal involves using web components as server components. While this feature is now available, we&#8217;re currently conducting several tests to fully integrate Stencil, Next.js and SSR.<\/p>\n<h3>Results<\/h3>\n<p>Results were observed immediately. Our Core Web Vitals rose from 72 to 90 compared to the Nuxt 2 implementation, with SEO scores jumping from 61 to 100.<\/p>\n<p>Loading improved 50% on average.<\/p>\n<h3>Migration logistics<\/h3>\n<p>Finding a technical solution is, however, only half the trouble. We could only generate value from this initiative once we had implemented the new stack throughout our codebase &#8211; a long and risky endeavor.<\/p>\n<p>Two teams were tasked with the migration: the engineering team in the sales squad, formally responsible for the website as well as other systems; and the engineering platform team.<\/p>\n<p>The first big decision was whether to migrate the entire project at once or to gradually refactor it. While both approaches required substantial effort, a gradual refactoring would allow us to parallelize the transition with the sales squad&#8217;s ongoing work. Besides, we could monitor performance and results for each functionality, enabling continuous learning and refining at every step.<\/p>\n<p>Ultimately, we chose to gradually refactor the entire project, starting with the homepage, assessing relevant metrics such as conversion and performance through A\/B tests. The end result was a smooth, quick, and successful implementation, setting the standard for future migrations.<\/p>\n","protected":false},"excerpt":{"rendered":"Como os times de engenharia da Alice trabalharam para implementar nova stack no site que melhorou o carregamento da p\u00e1gina em 50% e elevou a experi\u00eancia do usu\u00e1rio.","protected":false},"author":1,"featured_media":219,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-216","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-alice"],"acf":[],"_links":{"self":[{"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/posts\/216","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/comments?post=216"}],"version-history":[{"count":8,"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/posts\/216\/revisions"}],"predecessor-version":[{"id":252,"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/posts\/216\/revisions\/252"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/media\/219"}],"wp:attachment":[{"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/media?parent=216"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/categories?post=216"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alice.com.br\/tech\/wp-json\/wp\/v2\/tags?post=216"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}