<?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>Forem: Maksim Hodasevich</title>
    <description>The latest articles on Forem by Maksim Hodasevich (@dogfrogfog).</description>
    <link>https://forem.com/dogfrogfog</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%2Fuser%2Fprofile_image%2F1234006%2Fceeeeef3-9d86-4cf7-b390-f5f510aceec6.jpg</url>
      <title>Forem: Maksim Hodasevich</title>
      <link>https://forem.com/dogfrogfog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dogfrogfog"/>
    <language>en</language>
    <item>
      <title>Beyond the Basics: Exploring TailwindCSS and Linaria in Next.js - From Installation to Performance Optimization</title>
      <dc:creator>Maksim Hodasevich</dc:creator>
      <pubDate>Mon, 25 Dec 2023 12:01:00 +0000</pubDate>
      <link>https://forem.com/focusreactive/beyond-the-basics-exploring-tailwindcss-and-linaria-in-nextjs-from-installation-to-performance-optimization-h65</link>
      <guid>https://forem.com/focusreactive/beyond-the-basics-exploring-tailwindcss-and-linaria-in-nextjs-from-installation-to-performance-optimization-h65</guid>
      <description>&lt;p&gt;In the time we live in js web ecosystem is huge. Bigger than it has ever been.&lt;/p&gt;

&lt;p&gt;React applications, static websites, and dynamic front-end applications. There are a lot of approaches out there.&lt;/p&gt;

&lt;p&gt;But one thing remains the same - CSS. CSS is an essential part of every application.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS
&lt;/h3&gt;

&lt;p&gt;When it comes to CSS you want to solve multiple problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crafting CSS. Maintain simplicity, clarity, and manageability.&lt;/li&gt;
&lt;li&gt;Code organization. Maintain order and coherence in the styling.&lt;/li&gt;
&lt;li&gt;Design system and components styling. Ensuring a cohesive and efficient approach to styling becomes a big concern for all sizes of teams.&lt;/li&gt;
&lt;li&gt;HTML integration. How to embed CSS to the page.&lt;/li&gt;
&lt;li&gt;Performance optimization. It is a must if you want to reduce loading times and enhance user experience.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;New CSS frameworks appear as answers to those questions.&lt;/p&gt;

&lt;p&gt;Two of these modern frameworks are Tailwind and Linaria.&lt;/p&gt;

&lt;p&gt;Let's talk about an example of a Next.js app, see how they work, and compare them by the following categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installation and adoption&lt;/li&gt;
&lt;li&gt;How to solve real problems&lt;/li&gt;
&lt;li&gt;Ecosystems&lt;/li&gt;
&lt;li&gt;Organization and structure of components&lt;/li&gt;
&lt;li&gt;Build process and optimization&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Brief about both
&lt;/h3&gt;

&lt;p&gt;Tailwind is a functional CSS framework, that follows the utility-first approach.&lt;br&gt;
Linaria is a zero runtime CSS-in-JS library with a "Styled Components" syntax.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation and adoption
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Tailwind
&lt;/h4&gt;

&lt;p&gt;Now let’s discuss how to add a Tailwind to a brand new latest Next.js application. In FR we use Next.js because it’s the easiest way to get full control over rendering strategies and maximum performance out of the box.&lt;/p&gt;

&lt;p&gt;The easiest way to set up a new project with Tailwind is to run the command and go with the suggested CSS option, Tailwind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5bjnzxv9vatnyas5p09.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5bjnzxv9vatnyas5p09.gif" alt="CNA command" width="600" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If that approach doesn't suit you can follow a simple 6-step instruction from the &lt;a href="https://tailwindcss.com/docs/installation/using-postcss" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;, that takes less than a minute.&lt;/p&gt;

&lt;p&gt;It could be added to brand new projects, as well as be incrementally adopted with the same ease. This means that you can install Tailwind and start using it in some components while using your old approach to style all the other components.&lt;/p&gt;

&lt;p&gt;The simplicity of installation is one of the reasons why it’s so widely adopted and gaining so much traction.&lt;/p&gt;

&lt;h4&gt;
  
  
  Linaria
&lt;/h4&gt;

&lt;p&gt;Now let’s take a look at how easy to install Linaria to a fresh Next.js project that we have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @linaria/core @linaria/react @linaria/babel-preset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it. After that, you can go ahead and start using Linaria in your project. But it's too early to relax because, after that, we move to the 'Setup' stage.&lt;/p&gt;

&lt;p&gt;At the setup stage, you choose which bundler suits you and follow the instructions in the documentation. For example, here's a link to the webpack + Linaria documentation.&lt;/p&gt;

&lt;p&gt;But in the current article, we are more interested in Linaria + Next.js, so let’s see what else we should do.&lt;/p&gt;

&lt;p&gt;It's not that many steps. First of all, add a library called &lt;a href="https://www.npmjs.com/package/next-linaria" rel="noopener noreferrer"&gt;next-linaria&lt;/a&gt; with Linaria to your Next.js project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;next-linaria linaria
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, wrap your Next.js config with the &lt;em&gt;withLinaria&lt;/em&gt; function, and that’s it."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;withLinaria&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-linaria&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withLinaria&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;linaria&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* linaria options here */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="cm"&gt;/* next.js config here */&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to solve a real problem
&lt;/h3&gt;

&lt;p&gt;Before we go further let’s solve a simple task: "&lt;em&gt;Add a card with a title, description, and a button. The card should have a shadow and have rounded edges&lt;/em&gt;".&lt;/p&gt;

&lt;h4&gt;
  
  
  Tailwind
&lt;/h4&gt;

&lt;p&gt;When we are using Tailwind, the first thing we should think about to create new elements is HTML structure. Let’s write an HTML structure for the card.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardTailwind&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Card Tailwind&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                Lorem ipsum is placeholder text commonly used in the graphic.
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                CTA button
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the structure is ready we can add actual styles.&lt;/p&gt;

&lt;p&gt;Tailwind classes have different names. For example, few of names:: &lt;strong&gt;w-full&lt;/strong&gt;(width: 100%), &lt;strong&gt;m-12&lt;/strong&gt;(margin: 12rem), and &lt;strong&gt;pr-6&lt;/strong&gt;(padding-right: 6rem). At first, it might be frustrating to look up every class, but you will get used to it and understand the pattern really soon after start using it frequently.&lt;/p&gt;

&lt;p&gt;Let’s attach the styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardTailwind&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-96 gap-4 rounded p-5 shadow-xl"&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="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mb-4 text-2xl font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Card Tailwind&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mb-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                 Lorem ipsum is placeholder text commonly used in the graphic, print, and publishing industries for previewing layouts and visual mockups
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&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="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"rounded bg-black p-4 text-white"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                CTA button
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. We have a beautiful card.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faibm6pl7ylea4t7c2b7h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faibm6pl7ylea4t7c2b7h.jpg" alt="card-tailwind" width="476" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Linaria
&lt;/h4&gt;

&lt;p&gt;Now let’s do the same using Linaria.&lt;/p&gt;

&lt;p&gt;For the reimplementation of the component above using Linaria, you need to write "a little more" code. Let's take a look.&lt;/p&gt;

&lt;p&gt;Create CSS components and implement an HTML-like structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@linaria/react&lt;/span&gt;&lt;span class="dl"&gt;'&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;StyledWrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="s2"&gt;`
  width: 380px;
  padding: 20px;
  gap: 16px;
  border-radius: 4px;
  box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1),
    0 8px 10px -6px rgb(0 0 0 / 0.1);
`&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;StyledTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="s2"&gt;`
  margin-bottom: 16px;
  font-size: 24px;
  font-weight: 700;
  line-height: 32px;
`&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;StyledText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="s2"&gt;`
  margin-bottom: 16px;
`&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;StyledButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="s2"&gt;`
  padding: 16px;
  border-radius: 4px;
  background-color: rgb(0 0 0);
  color: rgb(255 255 255);
`&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;CardLinaria&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StyledWrapper&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;StyledTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; Lorem ipsum &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;StyledTitle&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;StyledText&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Lorem ipsum is placeholder text commonly used in the graphic, print, and
      publishing industries for previewing layouts and visual mockups
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;StyledText&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;StyledButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; CTA button &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;StyledButton&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;StyledWrapper&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Here is the result:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcatmh806i5jvxcjdxzi0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcatmh806i5jvxcjdxzi0.jpg" alt="card-linaria" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a good practice to separate "styled" elements from the JSX file for easier readability and to reduce the component's code. However, for a closer visual similarity between the two card structures, everything is included in one file.&lt;/p&gt;

&lt;p&gt;Although the steps and results are similar, the code looks different.&lt;/p&gt;

&lt;p&gt;The one written on Linaria is more intuitive because it utilizes regular CSS. At the same time amount of code is less if you use Tailwind.&lt;/p&gt;

&lt;p&gt;To understand Tailwind, you have to go through some learning first. Usually, it takes no less than a week to get used to a new syntax. In some cases that might be critical, for example, if you have a team of 100 engineers and each of them has to spend this week. Business might not want to go forward with this decision&lt;/p&gt;
&lt;h3&gt;
  
  
  Ecosystem
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Tailwind
&lt;/h4&gt;

&lt;p&gt;According to the "State of CSS 2023" survey, Tailwind CSS stands apart as the one major UI framework that developers are happy to keep using.&lt;/p&gt;

&lt;p&gt;More than 50% of developers are using Tailwind CSS over other CSS frameworks (CSS-in-JS has a separate category).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesp6rcjntt4vzb4l10fu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesp6rcjntt4vzb4l10fu.jpg" alt="state-of-css-2023-tailwind" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why is that, why do developers choose Tailwind over and over again?&lt;/p&gt;

&lt;p&gt;The Tailwind ecosystem is in a mature state right now, installable on every major web framework, and has a wide range of tools around it.&lt;/p&gt;

&lt;p&gt;Tailwind even hosted the conference earlier this year.&lt;br&gt;
Formatters and linters integrations to maintain the quality of your core. Great docs. Bunch of alternative educational materials, videos, and code examples.&lt;/p&gt;

&lt;p&gt;But there are more.&lt;/p&gt;

&lt;p&gt;Tailwind is not just a utility class framework. You can think about Tailwind like it is a design system. The system, that gives your UI "consistency" and offers essential defaults such as colors, typography, spacing, and breakpoints. It pushes for adopting best practices, such as using rem rather than px, as well as ensuring consistency in size, color palette, etc.&lt;/p&gt;

&lt;p&gt;But you still can customize values, used by one or another class, by overriding tailwind.config.js file.&lt;/p&gt;

&lt;p&gt;One more great thing about Tailwind, it allows you to copy-and-paste code snippet of any difficulty. You could copy button implementation to your project as well as copy the whole landing page.&lt;/p&gt;

&lt;p&gt;A big part of Tailwind’s ecosystem is UI components libs. But libraries could be different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Styled low-level components, such as buttons, inputs, datepickers etc. You can use these components either as the base layer of your design system or use default styles right away.&lt;/li&gt;
&lt;li&gt;Bigger components, combining multiple lower-level components. This could be long forms, tables, carousels, collections of cards, and so on. You have all blocks implemented and you just need to use them in the right order.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All UI components shipped with responsiveness. This feature alone significantly speeds up the development process. Also, a common big feature is theming. Having multiple themes for the component out of the box is a huge advantage and time saver&lt;/p&gt;

&lt;p&gt;Examples: &lt;a href="https://www.radix-ui.com/" rel="noopener noreferrer"&gt;radix-ui&lt;/a&gt;, &lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;chakra-ui&lt;/a&gt;, &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt;, &lt;a href="https://tailwindui.com/" rel="noopener noreferrer"&gt;tailwind-ui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tailwind is unopinionated about how you should structure your styles and components. You can use existing Tailwind classes such as &lt;strong&gt;mx-2&lt;/strong&gt;, &lt;strong&gt;shadow&lt;/strong&gt;, &lt;strong&gt;border-2&lt;/strong&gt; as well as use common CSS syntax using special &lt;strong&gt;[font-size:18px]&lt;/strong&gt; square brackets syntax. Also, you can put long classnames in its variable and UI components into its file.&lt;/p&gt;

&lt;p&gt;Let’s wrap it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tailwind helps you write CSS, but easier by providing DX and great defaults.&lt;/li&gt;
&lt;li&gt;Tailwind is a design system by itself, but if you want to implement your own design system - you can do it on top of it.&lt;/li&gt;
&lt;li&gt;You can copy, paste, and use a piece of ready code.&lt;/li&gt;
&lt;li&gt;Tailwind gives you the ability to choose what architectural approach to pursue.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Linaria
&lt;/h4&gt;

&lt;p&gt;Linaria is a zero-runtime CSS in JS library. Some of the problems that Linaria solves are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Linaria does not have any runtime cost, which means no extra JavaScript code to parse and execute, no style injection at runtime, and no dependency on any framework. This results in faster page loading and rendering, and lower memory usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility&lt;/strong&gt;: Linaria works with any JavaScript framework or library, such as React, Preact, and Svelte. It also supports server-side rendering (SSR) and code splitting out of the box. You can use Linaria with any bundler or toolchain, such as webpack, rollup, parcel, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DX&lt;/strong&gt;: Linaria lets you use familiar CSS syntax with CSS native nesting and custom properties. You can also use JavaScript for logic and variables, and lint your CSS in JS with stylelint. Linaria provides source maps for easier debugging and development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it comes to the difference between Tailwind and Linaria, there are also a few differences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Linaria doesn’t make you dependent on a specific design – you could develop any design you want.&lt;/li&gt;
&lt;li&gt;Linaria allows you to style components in separate files, making your code more readable. And you can always use your props for the styled-components to create a function that will provide you with different styles inside of the component.&lt;/li&gt;
&lt;li&gt;You can easily style child components in different ways: by wrapping them or using an SCSS-like style.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Organization and structure of components
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Tailwind
&lt;/h4&gt;

&lt;p&gt;When you developing a project, you probably have several reusable components that you use throughout the project. For example, It could be simple buttons and inputs, but it also could be more complex components such as double date pickers.&lt;/p&gt;

&lt;p&gt;When you create a UI kit with Tailwind you can use the basic approach of putting components in the special folder, let’s say &lt;em&gt;ui/components&lt;/em&gt;. And this folder will include all of your components: &lt;em&gt;ui/components/button.tsx&lt;/em&gt;, &lt;em&gt;ui/components/input.tsx&lt;/em&gt;, &lt;em&gt;ui/components/select.tsx&lt;/em&gt; and etc.&lt;/p&gt;

&lt;p&gt;Let’s take a look at a button component and see how we can implement custom styles as you would do in the project.&lt;/p&gt;

&lt;p&gt;Initially, the button will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ui/components/button.tsx&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;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="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step of course is to add some styles to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ui/components/button.tsx&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
  &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"px-4 py-6 rounded text-white bg-black"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;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="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&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 of course, it is a button, so it could have multiple variants: primary and secondary(you can increase the number of customizable params, but we will limit it to 1, &lt;em&gt;variant&lt;/em&gt;). To implement this you can use any library for combining classnames, for example, &lt;a href="https://www.npmjs.com/package/classnames" rel="noopener noreferrer"&gt;classnames&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/clsx" rel="noopener noreferrer"&gt;clsx&lt;/a&gt;. Let’s use the classic one, "classnames".&lt;/p&gt;

&lt;p&gt;Your code ends up being look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ui/components/button.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cn&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;classnames&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
  &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;cn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-4 py-6 rounded &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-white bg-black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-black bg-white border border-black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&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;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;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="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to allow additional customization, you can add &lt;strong&gt;classname&lt;/strong&gt; prop to your button and pass it to button.className.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ui/components/button.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cl&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;classnames&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
  &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;cl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px-4 py-6 rounded &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-white bg-black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-black bg-white border border-black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;className&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;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;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="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&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 this approach leads to certain problems. Classnames don't filter Tailwind classes and passed className might have conflicts with existing within the button classes.&lt;/p&gt;

&lt;p&gt;For example, if you want to use a button, and pass a custom padding to it, you probably will do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"secondary"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"px-2 py-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  clear
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&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;But in this case, you won't achieve the desired result. "px-2 py-3" conflicts with the default "px-4 py-6".&lt;/p&gt;

&lt;p&gt;In this case, both vertical and horizontal paddings will be applied. The bigger value wins in this case and padding selector weight should be increased using important, which is awful.&lt;/p&gt;

&lt;p&gt;How to avoid this problem?&lt;/p&gt;

&lt;p&gt;You can use &lt;a href="https://www.npmjs.com/package/tailwind-merge" rel="noopener noreferrer"&gt;tailwind-merge&lt;/a&gt; or a similar package for that. The idea behind this is you wrap your classes with a special util function, in this case, &lt;strong&gt;twMerge&lt;/strong&gt;. It will automatically remove classname duplicates and will leave the one that is defined last.&lt;br&gt;
So you can have your own &lt;strong&gt;cn&lt;/strong&gt; utility function, that will include twMerge inside.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ui/utils.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cn&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;classnames&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;twMerge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tailwind-merge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cn&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;classNames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;twMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;classNames&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, this &lt;strong&gt;cn&lt;/strong&gt; util is a nice and clean way to encapsulate twMerge logic. The good thing about this approach is - to implement it, all you should do is add new util and change imports.&lt;br&gt;
This is the most common thing devs forget when starting with Tailwind.&lt;/p&gt;

&lt;p&gt;Also, you could follow another approach, and put every property you want to customize in a named variable. For example, paddings could be "small" and "big” and they are controlled using a special "size" property, the same as a variant.&lt;/p&gt;

&lt;p&gt;Tailwind gives you a lot of freedom, but you should use this freedom wisely.&lt;/p&gt;
&lt;h4&gt;
  
  
  Linaria
&lt;/h4&gt;

&lt;p&gt;And now let's see how you could implement the same universal Button using Linaria.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Button/styled.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@linaria/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StyledButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="s2"&gt;`
  padding: 16px;
  border-radius: 4px;
  background-color: black;
  color: white;
`&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StyledButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;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;StyledButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the exact component, there's less code, and there's no need to use the &lt;strong&gt;className&lt;/strong&gt; for styling. All CSS goes into a separate file, or at least into a separate variable, which makes code reading much easier.&lt;/p&gt;

&lt;p&gt;Now, let's take a look at how we can create different variants of this button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Button/styled.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@linaria/react&lt;/span&gt;&lt;span class="dl"&gt;'&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;StyledButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="s2"&gt;`
    padding: 16px;
    border-radius: 4px;
    background-color: &lt;/span&gt;&lt;span class="p"&gt;${({&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff6aae&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secondary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;return&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff52a3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}}}&lt;/span&gt;&lt;span class="s2"&gt;;
    color: white;
`&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StyledButton&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;variant&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="nx"&gt;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;StyledButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Now we have a beautiful button that gets styled by its variants. For sure you could write a ternary statement but for scalability demonstration, I chose the switch-case here. In some unexpected cases when we don't have variant styles, they will fall back to default styles. And all that without using external libraries such as &lt;strong&gt;classNames&lt;/strong&gt; and ternary conditions.&lt;/p&gt;

&lt;p&gt;Also, we can make components inheritance like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Button/styled.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StyledButtonPrimary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;StyledButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;`
    background-color: #ff6aae;
`&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StyledButtonSecondary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;StyledButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;`
    background-color: #ff52a3;
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's how you create new styled components based on the old ones. The new components will inherit all the styles from their parent components and have rewritten background colors. This is another way of implementing component variations. But don't forget that when you change the parent, the child also changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tailwind
&lt;/h3&gt;

&lt;p&gt;Tailwind is building CSS into a single file.&lt;/p&gt;

&lt;p&gt;You might think: "It will build all classes, it’s a lot of them, right?"&lt;/p&gt;

&lt;p&gt;Yes, but Tailwind includes only used classes in the bundle. So if you are using a single class, let’s say  &lt;code&gt;text-lg&lt;/code&gt;, in the whole project, the resulting bundle will include only a single class. Adding prefixes version of CSS automatically happens because of the &lt;a href="https://www.npmjs.com/package/autoprefixer" rel="noopener noreferrer"&gt;autoprefixer&lt;/a&gt; postcss plugin. &lt;/p&gt;

&lt;p&gt;In addition to that, you can go further in terms of performance.&lt;/p&gt;

&lt;p&gt;Since Tailwind is a  PostCSS plugin, we can also add &lt;a href="https://www.npmjs.com/package/cssnano" rel="noopener noreferrer"&gt;cssnano&lt;/a&gt; plugin to compress the bundle.&lt;/p&gt;

&lt;p&gt;Drawback: can't build conditional classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linaria
&lt;/h3&gt;

&lt;p&gt;The main feature of Linaria is that it is zero-runtime and all CSS is extracted at build time. It allows you to write CSS code inside JavaScript files, but instead of bundling the CSS with JavaScript, it extracts the styles into a separate CSS file during the build process. This way, you can enjoy the benefits of CSS in JS, such as dynamic prop-based styles, without adding any runtime overhead or increasing the bundle size.&lt;/p&gt;

&lt;p&gt;At all other points, Linaria has the same pros and cons as &lt;strong&gt;styled-components&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Tailwind gave a huge push to the development of functional frameworks. New frameworks take beneficial parts of the existing technologies and mix them with problem-solving logic.&lt;/p&gt;

&lt;p&gt;For example &lt;a href="https://www.npmjs.com/package/twin.macro" rel="noopener noreferrer"&gt;twin.macro&lt;/a&gt;, which is CSS-in-JS solution but with tailwind classnames. Or another one, &lt;a href="https://www.npmjs.com/package/unocss" rel="noopener noreferrer"&gt;unocss&lt;/a&gt;. It gives you space to implement your own utility classes or use existing presets.&lt;/p&gt;

&lt;p&gt;In summary, Linaria stands out as a versatile tool for modern web development, offering a seamless solution to styling challenges with its unique blend of runtime CSS-in-JS and prop-based styling options. Its concise syntax and minimal bundle size make Linaria an optimal choice, striking a balance between performance, maintainability, and developer experience in the dynamic world of front-end development.&lt;/p&gt;

&lt;p&gt;When choosing a solution for your projects, as always, there are pros and cons to each of them. In some cases, you prioritize speed and want to go with the tailwind default structure. Sometimes, you have a huge project with styled components and you want to optimize and increase performance by removing run-time dependency, and you would prefer to use Linaria, over rewriting the whole project.&lt;/p&gt;

&lt;p&gt;So to make the right decision, you need to understand the details of 3 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the problem you solve&lt;/li&gt;
&lt;li&gt;the team you work with&lt;/li&gt;
&lt;li&gt;the project you develop&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this knowledge, you should consider all aspects of CSS, and how this will play with your system and help you achieve ultimate results and performance.&lt;/p&gt;

&lt;p&gt;In FocusReactie, as optimization and performance specialists, we do a deep analysis of the project and requirements before making any decision. This helps our clients achieve maximum results and gives their users an instantaneous experience when surfing their websites.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tailwindcss</category>
      <category>css</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Enhancing Content Creation: Visual Editing with Sanity and Storyblok for Next.js</title>
      <dc:creator>Maksim Hodasevich</dc:creator>
      <pubDate>Thu, 21 Dec 2023 11:45:00 +0000</pubDate>
      <link>https://forem.com/focusreactive/enhancing-content-creation-visual-editing-with-sanity-and-storyblok-for-nextjs-279a</link>
      <guid>https://forem.com/focusreactive/enhancing-content-creation-visual-editing-with-sanity-and-storyblok-for-nextjs-279a</guid>
      <description>&lt;p&gt;As experts in headless CMS (hCMS) and performance at FocusReactive, we work with many products from this segment and closely monitor their development. But most often we choose those CMS that have integration with such a framework as &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;. While hCMS makes it possible to separate content and work with it independently, Next.js helps to achieve maximum performance using the built-in mechanisms and capabilities of the framework.&lt;/p&gt;

&lt;p&gt;Sounds great, right?&lt;/p&gt;

&lt;p&gt;If we talk specifically, then two players can be distinguished here: Sanity and Storyblok.&lt;/p&gt;

&lt;p&gt;Another reason to go with either Sanity or Storyblok is that they offer visual editing functionality. Let’s dive into it and take a look at some of the main features they offer.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Visual Editing?
&lt;/h3&gt;

&lt;p&gt;Visual editing is a user-friendly method of creating and modifying digital content using graphical interfaces. It allows users, regardless of technical expertise, to directly interact with and manipulate content. Unlike traditional text-based editing, visual editing provides a real-time preview of how the content will appear on the page, increasing efficiency and collaboration among engineering and content teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  User Interface and Navigation
&lt;/h3&gt;

&lt;p&gt;Both Storyblok and Sanity have quite similar UI regarding visual editing: you will find the URL of the page, a preview of the page, and a list of blocks used by the page. It is important because this is one step towards standardization, even though non-official.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feij0vvwpxyfo26gsp03g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feij0vvwpxyfo26gsp03g.png" alt="visual editing layout" width="800" height="817"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The visual editors of both platforms allow you to click on any component and open it in the content column on the right side of the page. Here is how it looks:&lt;/p&gt;

&lt;p&gt;Storyblok&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzier8wr6nwv6b7mtalw5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzier8wr6nwv6b7mtalw5.gif" alt="storyblok - open content bar" width="720" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sanity&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7og8nx1blsec61atb8y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz7og8nx1blsec61atb8y.gif" alt="sanity - open content bar" width="600" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This feature saves time and speeds up the editing experience, especially when dealing with complex components that consist of multiple nested components.&lt;/p&gt;

&lt;p&gt;Both platforms will open top-level fields as well as nested child components. Depth doesn’t matter.&lt;/p&gt;

&lt;p&gt;Moreover, in the case of Storyblok you will see the nesting hierarchy aka breadcrumbs. They are clickable, which gives you additional convenience when editing/navigating between nested components. Sanity doesn’t have that.&lt;/p&gt;

&lt;p&gt;However, there are a couple of differences between the two platforms in the scope of this feature.&lt;/p&gt;

&lt;p&gt;Let’s say you have a link and you want to go to another page by clicking it. It won’t work, because instead of going to another page, you will open the clicked component in the content column.&lt;/p&gt;

&lt;p&gt;Sanity offers an "edit" toggler that turns it off and allows you to interact with the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2sr80e6x8mdupzv246y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2sr80e6x8mdupzv246y.gif" alt="sanity - edit toggler" width="600" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This feature is particularly useful when you want to preview and test the final result, including clicking links.&lt;/p&gt;

&lt;p&gt;Unfortunately, Storyblok does not have this feature. But, at least, you can do something. Specifically, you can prevent the iframe events, like clicking on a link. To configure that you can use &lt;em&gt;preventClicks&lt;/em&gt; property of &lt;em&gt;StoryblokBridge&lt;/em&gt; instance (check out &lt;a href="https://www.storyblok.com/docs/Guides/storyblok-latest-js#usage-of-storyblokbridge" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;). But this is more of a technical change, that should be done in code, rather than in UI. So the content team can’t use it independently.&lt;/p&gt;

&lt;p&gt;This can be frustrating in certain cases, especially when you develop a new functionality related to link behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Preview and Instant Feedback
&lt;/h3&gt;

&lt;p&gt;Both Storyblok and Sanity provide instant preview feedback when changing content. This means that any modifications made to components are reflected in real-time without any delay. This feature allows content creators to see exactly how their changes will affect the final appearance of the page, facilitating quick iterations and adjustments.&lt;/p&gt;

&lt;p&gt;The importance of that is elegantly described in Sanity's blog as &lt;a href="https://www.sanity.io/blog/introducing-presentation#1eb84f5d96d6" rel="noopener noreferrer"&gt;What you see is what you edit&lt;/a&gt;. Both hCMS play well in this crucial field.&lt;/p&gt;

&lt;p&gt;Let's see how it looks:&lt;/p&gt;

&lt;p&gt;Storyblok&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuv0458wjme1yn5hs369.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuv0458wjme1yn5hs369.gif" alt="storyblok - instant feedback" width="600" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sanity&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faperb34hsvwx24sggi9e.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faperb34hsvwx24sggi9e.gif" alt="sanity - instant feedback" width="600" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They are both fast and convenient to use.&lt;/p&gt;

&lt;p&gt;But what about applied changes persistence?&lt;/p&gt;

&lt;p&gt;Sanity saves all changes with draft status, so if you reload the page, changes will be there. This could be cool when you working on the content and don't want to lose any changes, but might be annoying when doing some development/debugging work.&lt;/p&gt;

&lt;p&gt;Storyblok, in turn, shows all the applied changes but doesn’t save them until you press "save" button.&lt;/p&gt;

&lt;p&gt;Worth to mention tho, is that Sanity gives you the ability to edit any separate component, eg footer and header, and see instant feedback. Same behavior as all the other fields.&lt;/p&gt;

&lt;p&gt;In case of Storyblok, the mechanism is a bit different. Let’s consider adding a reusable header component which you will share among the pages. You create a "story" (collection of fields) with header info and add a story field to the page. Then you should select one of the existing header stories as the value. And the last part is adding the header implementation in the code to make it work for all pages (eg use in layout, that is shared between pages).&lt;/p&gt;

&lt;p&gt;This is not easy and not straightforward. This approach limits your preview and live editing experience for shared components, which is not what you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drag-and-drop and Copying Functionality
&lt;/h3&gt;

&lt;p&gt;Drag-and-drop functionality is a must-have for any visual editor, and both Storyblok and Sanity offer this feature out of the box. It allows users to easily change the composition of a component and experiment with different layouts and designs. This intuitive and handy way of editing enhances the user experience and speeds up the content creation process.&lt;/p&gt;

&lt;p&gt;Storyblok&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7qu5i9q4jh5zzwscmor.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7qu5i9q4jh5zzwscmor.gif" alt="storyblok - drag-and-drop" width="560" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sanity&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny98i7rxngfk665f21i9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny98i7rxngfk665f21i9.gif" alt="sanity - drag-and-drop" width="600" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the efficiency of content creation is further elevated through the copying capabilities.&lt;/p&gt;

&lt;p&gt;Storyblok has a convenient mechanism to copy and paste components through a clipboard. It allows copy-paste components across projects/pages/nested objects. Meaning that you can copy a component and paste it to another page and even to another project. On the other hand, Sanity opts for a more targeted approach, offering duplication functionality. This means you could create a component copy that will be added right next to the original component.&lt;/p&gt;

&lt;p&gt;Let’s see the difference:&lt;/p&gt;

&lt;p&gt;Storyblok&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2h9vqhgc2gvtmknvbij.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2h9vqhgc2gvtmknvbij.gif" alt="storyblok - copying" width="560" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sanity&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftgug000vl72fnj74k34y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftgug000vl72fnj74k34y.gif" alt="sanity - duplication" width="600" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my opinion, Storyblok has a more convenient way of doing it. Because you can choose what components to copy (yes, you can select multiple components and copy them the same way you copy a single one) and where to paste it.&lt;/p&gt;

&lt;p&gt;Sanity is limited to copying only within the array and one component at a time, which can cause some inconvenience in certain cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preview for Different Viewports
&lt;/h3&gt;

&lt;p&gt;Another essential feature of visual editing tools is the ability to preview content for different viewports. For now, Sanity offers two options: desktop and mobile. While this provides some flexibility, it is limited compared to Storyblok. Storyblok allows users to preview content in mobile, tablet, and desktop viewports. Additionally, Storyblok enables users to preview content at any custom width, similar to widening the browser window. This level of customization ensures that content creators can accurately preview content across various devices and screen sizes.&lt;/p&gt;

&lt;p&gt;Storyblok&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzy8uj9uazdrq3ab8dzu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzy8uj9uazdrq3ab8dzu.gif" alt="storyblok - responsive view" width="600" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sanity&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yaor73p5l1dh9qa9yki.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yaor73p5l1dh9qa9yki.gif" alt="sanity - responsive view" width="600" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Collaboration and Communication
&lt;/h3&gt;

&lt;p&gt;Collaboration is key in any content creation process, and Sanity takes it a step further. In Sanity, users can work alongside teammates without any worries about overwriting, locking each other out, or applying unexpected edits. Fields are independent, so multiple users can edit different parts of the same page without conflicts. Even more, Sanity shows who is editing a document in real-time and it works with live preview as well. (Wow!)&lt;/p&gt;

&lt;p&gt;A common, but must-have feature, that is integrated in Sanity is document history. It keeps track of all the changes and you can always restore the particular version of any content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn31nlhar9mfyq7tj8z9m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn31nlhar9mfyq7tj8z9m.gif" alt="sanity - collab" width="600" height="795"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Storyblok offers some of these features as well. It supports the same history feature, with the ability to restore specific page versions. However multiple users can’t edit the same page at the same time, because conflicts will occur. This is a significant limitation for the content team.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F046jk6qnx4o0tvvx2eny.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F046jk6qnx4o0tvvx2eny.gif" alt="storyblok - collab" width="600" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One more great thing about both platforms is that you can leave comments on any component fields, tag team members, and ask questions or provide feedback directly within the editor. Tagged team members receive notifications, eliminating the need to switch between messaging platforms like Slack. This is nice because sometimes you don’t want to switch contexts and get distracted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Components composition
&lt;/h3&gt;

&lt;p&gt;Both Storyblok and Sanity empower content teams to create multiple pages from existing components. This flexibility allows for efficient content creation and promotes consistency across the website or application.&lt;/p&gt;

&lt;p&gt;However, it's important to note that the more pages you have, the more complexity lies with the content team. Changing components for one page could potentially affect other pages.&lt;/p&gt;

&lt;p&gt;Sanity has addressed this issue by providing a clear overview of which pages use the content and how changes will cascade. This feature allows us to inspect the results on pages before making any changes public.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhfa0x726maoh8hz0dhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhfa0x726maoh8hz0dhp.png" alt="sanity - used on pages" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, Storyblok does not offer this feature, which can be a drawback when working with a large project. This is what I'm really missing in Storyblok.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation and set up
&lt;/h3&gt;

&lt;p&gt;Implementation and Setup  The implementation of Sanity's visual editing involves using a new web standard called &lt;a href="https://www.sanity.io/docs/content-source-maps" rel="noopener noreferrer"&gt;Content source map&lt;/a&gt; and the &lt;a href="https://www.npmjs.com/package/@vercel/stega" rel="noopener noreferrer"&gt;@vercel/stega&lt;/a&gt; package for encoding and decoding page content. Other hCMS also adopt this approach for implementing visual editing.&lt;/p&gt;

&lt;p&gt;Similarly, Storyblok has its own implementation, requiring the manual addition of special properties to each editable block by using the special helper function.&lt;/p&gt;

&lt;p&gt;Both platforms use iframes in their implementation, making the setup steps quite similar. Here's a breakdown of the steps for each platform:&lt;/p&gt;

&lt;p&gt;For Sanity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;a href="https://www.sanity.io/docs/presentation" rel="noopener noreferrer"&gt;presentationTool&lt;/a&gt; to the project.&lt;/li&gt;
&lt;li&gt;Add an API route to manage draft/published documents.&lt;/li&gt;
&lt;li&gt;Connect your frontend application to the Sanity Studio.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a more detailed guide and analysis, you can refer to our article: &lt;a href="https://focusreactive.com/sanity-visual-editing-review/#setting-up-the-presentation-tool" rel="noopener noreferrer"&gt;A Deep Dive into Sanity's Visual Editing and Presentation Tool: The developer view&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For Storyblok:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add an API route to manage draft/published pages.&lt;/li&gt;
&lt;li&gt;Add a preview URL to Storyblok.&lt;/li&gt;
&lt;li&gt;Configure tokens.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For additional guidance, follow the &lt;a href="https://www.storyblok.com/tp/create-a-preview-environment-for-your-next-js-website" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In summary, the general process involves setting up a route to handle incoming requests and configuring the tools offered by either Sanity or Storyblok.&lt;/p&gt;

&lt;p&gt;It's interesting to note that both products follow a similar approach. This not only validates the idea but also establishes a pattern, which is beneficial for developers and community users. Patterns are always a positive aspect!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;For headless CMS, visual editing tools play a crucial role in simplifying content creation, developing new features, and testing the result.&lt;/p&gt;

&lt;p&gt;Both Storyblok and Sanity offer powerful and intuitive visual editors that empower content and engineering teams. While Storyblok was the pioneer in providing a visual editing experience out of the box, Sanity has entered the scene with its own set of impressive features.&lt;/p&gt;

&lt;p&gt;Although both visual editing tools are very similar, you can’t decide which CMS to pick based on only this set of features. A lot of aspects should be taken into consideration before making a final call: tech stack, size and experience of the team, budget, level of communication between teams, need for customization, how good documentation is, potential risks, and more.&lt;/p&gt;

&lt;p&gt;Check out related articles, that might help you make a final decision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://focusreactive.com/storyblok-localization-part1/" rel="noopener noreferrer"&gt;International and multilingual sites with StoryBlok and Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://focusreactive.com/sanity-cms-for-tipico-platform/" rel="noopener noreferrer"&gt;Project insides: How We Built a Complex Headless CMS Based on Custom Sanity Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://focusreactive.com/best-headless-cms-for-nextjs/" rel="noopener noreferrer"&gt;Best Headless CMS for NextJS in 2023&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://focusreactive.com/how-to-choose-storyblok-partner/" rel="noopener noreferrer"&gt;How to Choose a Storyblok Partner&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, as always, the choice is dependent on your specific use case.  You should understand your system and business deeply, to make the right decision.&lt;/p&gt;

&lt;p&gt;If you have any questions, &lt;a href="https://focusreactive.com/#mail-us" rel="noopener noreferrer"&gt;contact us&lt;/a&gt;, headless CMS experts, and we will help you to make this choice easier.&lt;/p&gt;

</description>
      <category>sanity</category>
      <category>storyblok</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
