<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: FindLabs</title>
    <description>The latest articles on DEV Community by FindLabs (@findlabs).</description>
    <link>https://dev.to/findlabs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F8754%2F732b7468-01c1-4424-ba2a-f8b70fff9253.png</url>
      <title>DEV Community: FindLabs</title>
      <link>https://dev.to/findlabs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/findlabs"/>
    <language>en</language>
    <item>
      <title>Darling, I converted our perfectly fine SPA application into SSR: Part 2</title>
      <dc:creator>Max Daunarovich</dc:creator>
      <pubDate>Tue, 14 May 2024 09:59:54 +0000</pubDate>
      <link>https://dev.to/findlabs/darling-i-converted-our-perfectly-fine-spa-application-into-ssr-part-2-185n</link>
      <guid>https://dev.to/findlabs/darling-i-converted-our-perfectly-fine-spa-application-into-ssr-part-2-185n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you're just tuning in, the &lt;a href="https://dev.to/findlabs/flowdiver-the-road-to-ssr-part-1-448k"&gt;first chapter&lt;/a&gt; detailed our approach to creating the SPA (Single Page Application) version of &lt;a href="https://www.flowdiver.io/"&gt;Flowdiver&lt;/a&gt;. We explained the reasoning behind our decisions and the challenges we encountered.👍&lt;/p&gt;

&lt;p&gt;In this chapter, we'll discuss our experiences in migrating the SPA to a Next.js framework, employing an SSR (Server-Side Rendering) strategy to tackle some of the ongoing "problems."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lab
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;To ensure that our decision was the right one, we took some time to evaluate other existing solutions on the market.&lt;/p&gt;

&lt;p&gt;Next.js stood out due to its large community, the abundance of examples, and the availability of templates. However, the "recent" shift from a pages router to an app router, along with the introduction of client and server components, and the overall feeling that Next.js might be too cumbersome for our needs, sent us on a quest for new horizons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qwik
&lt;/h3&gt;

&lt;p&gt;We just wrapped up our &lt;a href="https://www.findlabs.io/"&gt;FindLabs corporate website&lt;/a&gt; using Qwik and thought we could utilize our newly acquired experience.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros
&lt;/h4&gt;

&lt;p&gt;Qwik was incredibly fast and straightforward to set up for SEO and achieving good web metrics. The clear separation between client and server-side code, utilizing &lt;code&gt;useVisibleTask$()&lt;/code&gt; was a breeze to manage and didn't require complex mental gymnastics to understand the migration process.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cons
&lt;/h4&gt;

&lt;p&gt;The SPA version heavily utilizes &lt;a href="https://styled-components.com/"&gt;Styled Components&lt;/a&gt;, and although it's feasible to use the &lt;code&gt;styled-vanilla-extract&lt;/code&gt; library and migrate the code with minimal changes, some parts would still need refactoring since CSS is pre-built during compilation. We've previously used the &lt;code&gt;useStylesScoped$&lt;/code&gt; function while building a corporate website, but it often felt more like a hack than a solid solution.&lt;/p&gt;

&lt;p&gt;Another significant challenge was the &lt;a href="https://www.patterns.dev/react/render-props-pattern/"&gt;render props&lt;/a&gt; pattern. We heavily rely on this for our table component to ensure a high level of customization and abstraction. However, Qwik's architecture is fundamentally different—the framework aims to fragment the code into the smallest possible chunks and load them in parallel. This means it attempts to &lt;a href="https://qwik.dev/tutorial/props/closures/"&gt;serialize your render functions&lt;/a&gt;, which becomes a mess real fast.&lt;/p&gt;

&lt;p&gt;Additionally, the contexts that Flowdiver heavily depends on would be unworkable and would require a complete redesign into a new composition. While these changes are manageable, we wanted to minimize disruption as much as possible.&lt;/p&gt;

&lt;p&gt;Considering all these pros and cons, we decided to put this on the bench for a better use case 🙏.&lt;/p&gt;

&lt;h3&gt;
  
  
  Waku
&lt;/h3&gt;

&lt;p&gt;While researching server-side components, we discovered &lt;strong&gt;&lt;a href="https://waku.gg/"&gt;Waku&lt;/a&gt;&lt;/strong&gt;—a minimal React framework that facilitates the use of SSR (Server-Side Rendering) and SSG (Static Site Generation) approaches effortlessly 👍.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros
&lt;/h4&gt;

&lt;p&gt;Waku struck the perfect balance we were looking for—a compact footprint on both client and server sides, along with a clear division of domains.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://waku.gg/"&gt;front page&lt;/a&gt; did an excellent job of clearly and efficiently explaining the concepts of client-/server-side components: data fetching, SEO, routing—all the puzzle pieces began to fall into place.&lt;/p&gt;

&lt;p&gt;We bootstrapped an app and attempted a minimal setup migration. The community and the author on Discord were incredibly responsive, offering valuable tips and explanations on various concepts and strategies.&lt;/p&gt;

&lt;p&gt;Sounds like a dream, right? Well, yeah...&lt;/p&gt;

&lt;h4&gt;
  
  
  Cons
&lt;/h4&gt;

&lt;p&gt;The unstyled concept was functioning flawlessly, but we had to abandon &lt;code&gt;styled-components&lt;/code&gt; almost immediately. The documentation covered CSS modules well, but lacked information on styled components and similar approaches. At that point, we realized that sticking to a well-tried solution might backfire... 🙈&lt;/p&gt;

&lt;p&gt;Similar to &lt;code&gt;Qwik&lt;/code&gt;, the use of contexts in Waku also appeared to be challenging and required significant refactoring to manage props drilling effectively.&lt;/p&gt;

&lt;p&gt;Moreover, as we were experimenting with it, new features were continuously being released, making the framework feel somewhat unstable and experimental as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Next?
&lt;/h3&gt;

&lt;p&gt;In the end, we found ourselves back where we started, but with newfound knowledge and confidence in our choice.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Pros&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Next.js stands out as the most seasoned of all React-based full-stack frameworks. With dozens of examples and templates available, and hundreds of resolved issues on GitHub backed by multiple contributors, there's reassurance that it won’t just disappear over the weekend because the original author decided to move on from his project.&lt;/p&gt;

&lt;p&gt;It’s also a match made in heaven for Vercel deployment. True to its promises, some features (notably &lt;a href="https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering"&gt;PPR&lt;/a&gt;) are exclusively viable on Vercel due to the tight integration and finely tuned setup.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Cons&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Server-side components still didn't make much sense with us at that time 😅&lt;/p&gt;

&lt;p&gt;However, we decided it was time to shed our apprehensions and start tinkering!&lt;/p&gt;

&lt;h2&gt;
  
  
  Take 1 - Heads-on!
&lt;/h2&gt;

&lt;p&gt;We opted for the &lt;code&gt;app router&lt;/code&gt; as it appeared more modern and future-proof. Moreover, it seemed like a logical next step from &lt;a href="https://reactrouter.com/en/main"&gt;react-router&lt;/a&gt;. It also supports &lt;code&gt;server components&lt;/code&gt;, which were crucial for our application. Plus, the &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/parallel-routes"&gt;layout fragments&lt;/a&gt; really seemed promising!&lt;/p&gt;

&lt;p&gt;With our mindset focused on minimal changes and limited time commitment, we set up the basics that would allow us to work with styled-components and render them on a blank page. Once this foundation was laid, we began to &lt;code&gt;migrate&lt;/code&gt; (or more accurately, copy/paste 😅) our components into the Next.js-based repository.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One component, &lt;br&gt;
Two components, done! &lt;br&gt;
Errors during compilation, &lt;br&gt;
None!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well, not exactly. We were soon overwhelmed with errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;You&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;re importing a component that needs createContext. It only works in a Client
Component but none of its parents are marked with "use client", so they&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;re&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt;
&lt;span class="nx"&gt;Components&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

  &lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="nx"&gt;Learn&lt;/span&gt; &lt;span class="nx"&gt;more&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//nextjs.org/docs/getting-started/react-essentials&lt;/span&gt;
  &lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we came prepared, haha! We just needed to tag some files with &lt;code&gt;use client&lt;/code&gt;. So this one, that one... and that one too!&lt;/p&gt;

&lt;p&gt;In the end, we marked almost EVERYTHING with &lt;code&gt;use client&lt;/code&gt; and inadvertently exposed our Hasura token to the public domain. Not exactly what we had planned, but hey, at least it was functioning. A living and “breathing” system. Yes! 👍&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Take 2 - We Require More Minerals&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The next step was to migrate the rest of the pages. So, we thought, why not transfer about a hundred files at once? What could possibly go wrong? Fortunately, with our root layout file flagged as &lt;code&gt;use client&lt;/code&gt;, everything below it was also tagged as "dirty," and thus the compiler remained silent, untroubled by our actions.&lt;/p&gt;

&lt;p&gt;However, trouble started brewing as soon as we began refactoring pages into server-side components...&lt;/p&gt;

&lt;p&gt;Here’s the snag: &lt;code&gt;client components&lt;/code&gt; can’t import &lt;code&gt;server components&lt;/code&gt;. Yet, we desperately needed that &lt;code&gt;ThemeProvider&lt;/code&gt; from &lt;code&gt;styled-components&lt;/code&gt; to function correctly. Moreover, various other context providers needed to be positioned at the top, so they were accessible for data reading across the board.&lt;/p&gt;

&lt;p&gt;We plunged into the refactoring, attempting to determine what should remain a &lt;code&gt;server component&lt;/code&gt; and what needed to be client-side. Yet, a relentless cascade of - &lt;strong&gt;&lt;em&gt;It only works in a Client Component&lt;/em&gt;&lt;/strong&gt; - made it seem like we were running in circles, getting nowhere fast.&lt;/p&gt;

&lt;p&gt;It was time to pull back, reassess, and develop a new "pipeline" for how we would transition our SPA into the promised land of SSR.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Take 3 - Slow, but Steady&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We stripped everything down to the bare bones and began piecing the pages back together, labeling only the absolutely essential elements as client components. Fortunately, most of these necessities were related to &lt;code&gt;styled-components&lt;/code&gt; and nestled within the &lt;code&gt;styles.tsx&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Hooks, particularly &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useSearchParams&lt;/code&gt;, were reliable indicators that a file belonged on the client side of the network boundary. However, some could seamlessly receive &lt;code&gt;searchParams&lt;/code&gt; props directly from &lt;code&gt;page.tsx&lt;/code&gt; without switching allegiances.&lt;/p&gt;

&lt;p&gt;We tackled the migration one route at a time, and it all seemed to be going smoothly, until we encountered the more complex pages—like those for accounts or transactions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue7dw5l80wxbzxqy57x2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue7dw5l80wxbzxqy57x2.png" alt="Image description" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These pages featured some static elements—like status and transfers at the top—and more dynamic components controlled by tabs. We considered collecting all the data and distributing it to child components via context, but that approach would only be effective in this specific scenario. The account page, in particular, demanded more diverse data, which would necessitate pulling from the server or refreshing the page with new search parameters.&lt;/p&gt;

&lt;p&gt;This led us to the decision to utilize &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/parallel-routes"&gt;parallel routes&lt;/a&gt; for implementing the tabs. This strategy allowed us to establish clear distinctions in data fetching for each tab without the complication of tight coupling. As a result, the render tree remained neat and tidy, while all the logic was appropriately segregated within its own domains:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kx310nvfegc1aqrqf5y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kx310nvfegc1aqrqf5y.png" alt="Image description" width="279" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u5xbjilh0pzwel6ilra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u5xbjilh0pzwel6ilra.png" alt="Image description" width="201" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point &lt;code&gt;layout.tsx&lt;/code&gt; in the sub-route &lt;code&gt;/tx/[id]&lt;/code&gt; operates as a component, alongside &lt;code&gt;page.tsx&lt;/code&gt; and all the parallel routes. We used &lt;code&gt;Suspense&lt;/code&gt; to effectively display loading states, ensuring that user experience remains smooth and uninterrupted during data fetching. Importantly, we carefully manage security by not exposing our token when fetching data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbDZndTlod2tzcmR3Y2ZzdGh2NjV5OGI4Zm53dmhsNGZhb2d5cno1aSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/yJFeycRK2DB4c/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbDZndTlod2tzcmR3Y2ZzdGh2NjV5OGI4Zm53dmhsNGZhb2d5cno1aSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/yJFeycRK2DB4c/giphy.gif" alt="Noice" width="450" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Houston, We Have a Problem
&lt;/h2&gt;

&lt;p&gt;While most of the components and pages function as intended, some fall short...&lt;/p&gt;

&lt;p&gt;Take our custom table component, for instance. In the SPA setup, displaying the loading state was straightforward—we simply retrieved it from the context. However, in the SSR framework, components like the table header and pagination controls need to be visible before the data is fetched. This necessitated some serious refactoring and clever component rearrangement.&lt;/p&gt;

&lt;p&gt;Before, our context provider enveloped the table; now, this needs to be shifted into the &lt;code&gt;children&lt;/code&gt; block because data fetching occurs server-side, across the network boundary. We refactored the code, injecting additional props into the table (since it could no longer access these from the context) and ended up with the following configuration:&lt;/p&gt;

&lt;p&gt;While most of the components and pages works as intended, some don’t…&lt;/p&gt;

&lt;p&gt;For example, our bespoke table component. In SPA implementation it was easy to show loading state - we can simply pull it from context. But in SSR our table  - at least header and pagination controls - should be visible before data is fetched. That was asking for a refactoring and some component juggling.&lt;/p&gt;

&lt;p&gt;Previously, we had context provider wrapping up table, now it should be part of the &lt;code&gt;children&lt;/code&gt; block, cause data fetching is on server side of network boundary. We refactored that code, passed extra props to the table (cause now it couldn’t read them from context) and end up with following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyxk2b2nicb5zc0174ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyxk2b2nicb5zc0174ys.png" alt="Image description" width="452" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AccountTransactionsProvider&lt;/code&gt; is a server component that fetches the data and then passes it into &lt;code&gt;TableDataProvider&lt;/code&gt; via props. And &lt;code&gt;TableDataProvider&lt;/code&gt; is a client component that can properly work with contexts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8gjvv9hn5ijk8vufd47.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8gjvv9hn5ijk8vufd47.png" alt="Image description" width="517" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That was an &lt;code&gt;Aha!&lt;/code&gt; moment in understanding how to compose server and client components. You can’t &lt;code&gt;import&lt;/code&gt; server components from client boundary, BUT, you can render the result of their execution in your client components. You just need to pass them as &lt;code&gt;children&lt;/code&gt; or render props!&lt;/p&gt;

&lt;p&gt;So the following composition is perfectly fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RootServerComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;--------- SERVER */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClientComponent&lt;/span&gt; &lt;span class="na"&gt;renderProp&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SDFComponent&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;--------- CLIENT */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

                &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* the following code will be passed as "children" */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SmallServerComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;--------- SERVER */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;InnerClientComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;--------- CLIENT */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ServerCounterComponent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;--------- SERVER */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;InnerClientComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SmallServerComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

                &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... and then we can control where to put SDFComponent in layout */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ClientComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RootServerComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer not to manage this composition at the root level and aim for modularity, you can achieve this by enhancing your &lt;code&gt;ClientComponent&lt;/code&gt; to accept additional properties—for example, a &lt;code&gt;renderProp&lt;/code&gt;. You can then trust that it will include the necessary representation, allowing you to construct your layout in any preferred manner.&lt;/p&gt;

&lt;p&gt;Personally, I consider the &lt;code&gt;Render Props&lt;/code&gt; pattern to be one of React's most powerful features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Hurdle
&lt;/h2&gt;

&lt;p&gt;The very last “challenge” involved transitioning from the &lt;code&gt;useSearchParams&lt;/code&gt; hook provided by &lt;code&gt;react-router&lt;/code&gt; to its counterpart in &lt;code&gt;next.js&lt;/code&gt;. The twist with Next.js is that while it lets you read the search parameters, it doesn’t offer a direct setter. This requires a more hands-on approach involving a trio of hooks: &lt;code&gt;usePathname&lt;/code&gt;, &lt;code&gt;useRouter&lt;/code&gt;, and &lt;code&gt;useSearchParams&lt;/code&gt;. To manage this, you have to construct your own setter. The process would typically look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePathname&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// later in code&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We moved this into a convenient hook, so we could reuse the code and update multiple parameters simultaneously—a necessity for managing the filters in our table. &lt;/p&gt;

&lt;h2&gt;
  
  
  Cherries on top 🍒🍰
&lt;/h2&gt;

&lt;p&gt;Leveraging Next.js, a server-side solution, made setting OpenGraph metadata, titles, and descriptions for our pages simply a breeze. This final requirement was almost effortlessly fulfilled. However, we encountered a hiccup with &lt;code&gt;@vercel/og&lt;/code&gt;, which refused to render fonts properly, even the &lt;code&gt;bold&lt;/code&gt; variant of the default one.&lt;/p&gt;

&lt;p&gt;After some troubleshooting, we discovered that Noto Sans, included in the package, &lt;a href="https://github.com/vercel/satori/issues/263#issuecomment-1500705357"&gt;only supports a 400 weight&lt;/a&gt;. Attempts to load custom fonts from the file system did not pan out as expected—the documentation and examples lacked clear guidance on implementation. Furthermore, deployment introduced peculiar bugs, even though the local solutions worked just fine.&lt;/p&gt;

&lt;p&gt;Despite these challenges, experimenting with Satori (the library that lets you create SVG/PNG images by defining them with HTML) proved to be quite enjoyable. We would definitely consider using this package in future projects.&lt;/p&gt;

&lt;p&gt;Additionally, caching was readily available right out of the box, allowing us to lessen the load on our servers and enhance the overall user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Postmortem
&lt;/h2&gt;

&lt;p&gt;Aside from the initial confusion surrounding server components, the majority of the migration process unfolded smoothly.&lt;/p&gt;

&lt;p&gt;The groundwork established during the SPA implementation proved to be robust and resilient, carrying us through the migration with only minimal adjustments needed.&lt;/p&gt;

&lt;p&gt;Most of our headaches stemmed from &lt;code&gt;styled-components&lt;/code&gt;. Despite the merits of this CSS-in-JS library, it doesn’t mesh as seamlessly with server components. Traditional CSS methodologies, such as BEM (&lt;a href="https://getbem.com/"&gt;Block, Element, Modifier&lt;/a&gt;), CSS modules, or even utility-first frameworks like Tailwind, would have alleviated many of our difficulties. This lesson is a key takeaway that we plan to carry forward into our future projects.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>casestudy</category>
      <category>typescript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>This is how we've utilized our expertise and built solid data-driven application</title>
      <dc:creator>Max Daunarovich</dc:creator>
      <pubDate>Fri, 03 May 2024 15:34:55 +0000</pubDate>
      <link>https://dev.to/findlabs/flowdiver-the-road-to-ssr-part-1-448k</link>
      <guid>https://dev.to/findlabs/flowdiver-the-road-to-ssr-part-1-448k</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;One of the key projects &lt;a href="https://www.findlabs.io/"&gt;FindLabs&lt;/a&gt; has created and currently supports is &lt;a href="https://www.flowdiver.io/"&gt;FlowDiver&lt;/a&gt; - the Flow Blockchain block explorer. It allows you to inspect different information about the blockchain, without directly interfacing with it (which can require a specific setup and knowledge). For example, you will be able to explore which transactions were included in a recent block, what arguments and code were used for said transactions and what events were emitted as a side effect of changes to network’s state.&lt;/p&gt;

&lt;p&gt;There was a single solution on a market back then - &lt;strong&gt;FlowScan&lt;/strong&gt; - which was providing the same kind of information, but there were no public API projects could tap into and private one had it’s own issues - inconsistent data, complex data fetching or inability to filter specific results out, etc.&lt;/p&gt;

&lt;p&gt;We’ve seen the potential and interest in the community and started the work from the data layer. When we got the confirmation and understanding the gargantuan scale of the task we bootstrapped front-end app and continued from there.&lt;/p&gt;

&lt;p&gt;This post is about our journey - what decisions we’ve made and where they lead us. Grab a popcorn and prepare for a ride! :)&lt;/p&gt;

&lt;h1&gt;
  
  
  How we tailored it
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Pick your poison
&lt;/h2&gt;

&lt;p&gt;Given our team's collective proficiency within the React ecosystem, we decided to leverage this expertise for our project. Initially, we contemplated utilizing Next.js; however, due to the limited practical experience with this technology among key engineers and the pressing timeline to develop the first prototype, we opted for a Single Page Application(SPA) approach. For bundling, we selected &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt;, primarily due to its super fast build times, simplicity of configuration, and potential for a nearly seamless transition to server-side rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Styled Components
&lt;/h2&gt;

&lt;p&gt;Next key elements are &lt;code&gt;styled-components&lt;/code&gt; and accompanying it &lt;code&gt;styled-system&lt;/code&gt; libraries. These gave us a clear separation of CSS rules at the component level and provided a nice way to implement our building blocks. &lt;/p&gt;

&lt;p&gt;Yes, it increased the initial bundle size, but that chunk would be reusable and would only be loaded once. Additionally, lazy loading the routes allowed us to cleanly split the app into separate blocks, further minimizing the loading time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Atomic System
&lt;/h2&gt;

&lt;p&gt;We were loosely following &lt;a href="https://atomicdesign.bradfrost.com/table-of-contents/"&gt;Atomic Design&lt;/a&gt; principle to slice our interface into reusable parts. The smallest possible elements would be living under &lt;code&gt;components/atoms&lt;/code&gt;, bigger “brothers” and “sisters” would form &lt;code&gt;molecules&lt;/code&gt; which would be composed into &lt;code&gt;organisms&lt;/code&gt;.  This methodology provided a solid foundation and established component composition as the core principle of our design approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  React Router
&lt;/h2&gt;

&lt;p&gt;React Router v6 was picked as a stable routing solution to support lazy loaded routes, domain code splitting and it was potentially an easier way to port the app to Next.js. Following Atomic Design, we created a &lt;code&gt;pages&lt;/code&gt; folder and gathered files for respective routes there. In retrospect, this was a great decision, which made transition to server-side rendering a breeze. &lt;/p&gt;

&lt;h2&gt;
  
  
  State
&lt;/h2&gt;

&lt;p&gt;Application architecture is rather simple and can be seen as a lose composition of separate modules. Data fetched on a single route is never reused on another route, which makes it easy to describe using React’s &lt;code&gt;useState&lt;/code&gt; and urql’s &lt;code&gt;useQuery&lt;/code&gt; . Occasional calls to chain were sewn together with backend data using &lt;code&gt;useEffect&lt;/code&gt; resolvers. Overall, the system doesn’t require complex state management and data juggling, which made it easier to split tasks between team members and work in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;A good portion of the application consists of tabular representation of data. Data in said tables can be sorted, paginated and filtered. During the design stage we were able to identify repetitive patterns across these component and plan implementation accordingly. &lt;/p&gt;

&lt;p&gt;Disregarding props-drilling technique in favor of a more reliable and elegant solution we looked for inspiration elsewhere. Another project of ours &lt;a href="https://find.xyz/"&gt;.find&lt;/a&gt; was using &lt;a href="https://typesense.org/"&gt;Typesense&lt;/a&gt;/&lt;a href="https://www.algolia.com/"&gt;Algolia&lt;/a&gt; components, which looked a bit like black-box/magic, but at the same time provided a clean approach to build complex and highly customizable solutions.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;Table&lt;/code&gt; component lives within the confines of our data provider and can be expanded with pagination, sorting and filtering solutions. All of the aforementioned parts are decoupled from each other and could be customized for current needs. Some tables are using on-chain data and some use locally stored data. Filtering/sorting logic is purely client-side, while others pull filtered/sorted data directly from the backend and require customized variables for GraphQL queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hasura, urql and Graphql
&lt;/h2&gt;

&lt;p&gt;Our backend sports a huge Postgress database, which is exposed via Hasura service in the form of a GraphQL endpoint. This endpoint provides a schema which paired with &lt;a href="https://the-guild.dev/graphql/codegen"&gt;graphql-codegen&lt;/a&gt; generator to get accurate representation on the client side with all the necessary types and variables for the hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typescript
&lt;/h2&gt;

&lt;p&gt;While initially we fancied the idea of dropping Typescript to keep momentum and maximum velocity, in the end we are happy we didn’t go that route and decided to do it “right” from the start.&lt;/p&gt;

&lt;p&gt;TypeScript offers several benefits to a React project, particularly when collaborating with multiple team members. It introduces static typing, which helps catch errors during compile-time rather than at runtime. This not only enhances the quality of the code but also cuts down on the time needed for debugging. The clear typing system also makes the code more readable and acts as its own documentation, making it easier for team members to understand one another's work. Additionally, TypeScript provides robust tooling that includes autocompletion, type checking, and advanced refactoring capabilities, all of which significantly boost developer efficiency. Moreover, the use of generated types from codegen has allowed for the expansion of generic types, ensuring accurate typing of table elements. It's akin to watching puzzle pieces fit together perfectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Choosing &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt; was a natural decision as it has become the default method for launching apps that are accessible to a wide audience. The simplicity of configuring environment variables, domains, and other settings facilitated this choice. We have implemented feature branch deployment to guarantee that the code is operational and prepared for peer review.&lt;/p&gt;

&lt;h1&gt;
  
  
  What went “wrong”
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Exposed token
&lt;/h2&gt;

&lt;p&gt;We were initially okay with using an exposed token because there were ways to restrict token rights—either by limiting calls to only those from Flowdiver or by rate limiting the data and frequency of requests. However, this was not an ideal solution as we aimed for a scenario where the token would never be exposed to the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  No SEO whatsoever
&lt;/h2&gt;

&lt;p&gt;The technology we chose did not offer an easy way for pages to be indexed by search engines. The control over page titles was subpar—looking at you, Helmet—and definitely needed improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Over-engineering
&lt;/h2&gt;

&lt;p&gt;In our quest to perfectly compose complex blocks, we ventured a bit too far into premature optimization. It wasn’t disastrous, but it did mean that some aspects were overcomplicated when they could have been simpler and easier to maintain. We learned our lesson and refactored this overly complex code when we transitioned to a Next.js solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks limitations
&lt;/h2&gt;

&lt;p&gt;While the &lt;code&gt;useQuery&lt;/code&gt; hooks from urql provided straightforward and clean access to backend data, things got tricky when we needed to combine data from multiple sources. Hooks have to be called conditionally, which led to more complex logic involving one or more &lt;code&gt;useEffect&lt;/code&gt; hooks. Although urql allows you to call a query directly and convert the result to a promise, hooks were more convenient, so we endured the complexity and later refactored in Next.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Atomic, shmatomic…
&lt;/h2&gt;

&lt;p&gt;We did our best to adhere to the Atomic design approach when creating components. However, the system sometimes didn’t clearly indicate where components should be placed. In some cases, it was obvious that an &lt;strong&gt;&lt;code&gt;atom&lt;/code&gt;&lt;/strong&gt; could go anywhere, but in other scenarios, it made more sense to position it near a &lt;strong&gt;&lt;code&gt;molecule&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;code&gt;organism&lt;/code&gt;&lt;/strong&gt;, especially if it was seldom reused.&lt;/p&gt;

&lt;p&gt;This approach didn’t break the app but certainly led to an uneasy feeling that the next person to maintain it might face some challenges and a somewhat rough path.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Next?
&lt;/h1&gt;

&lt;p&gt;As we continued to roll out new features, we began to explore what it would take to migrate everything to Next.js. This move could address some of the fundamental issues we faced with our current setup and provide a clearer path for the app's growth.&lt;/p&gt;

&lt;p&gt;Find out how we did it and the challenges we faced in the next chapter! 😉&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>frontend</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
