From React to Hotwire - Part II - [EN]
Tags: react, hotwire, rails, phlex
Published: 2024-06-16 15:50:03

[Versão em Português](https://dev.to/cirdes/do-react-ao-hotwire-parte-ii-pt-br-3aa4)

## Introduction

In the [first article](https://dev.to/cirdes/from-react-to-hotwire-part-i-en-2o6g) about this migration from React to Hotwire, I discussed how we arrived at our current React stack. In this Part II, I will talk about the return to Rails views at Linkana.

## Evolution of Rails and SSR

One of Rails' most brilliant characteristics is its obsession with simplicity. Since Rails was born within [Basecamp](https://brasil.basecamp.com/), a company that chose to have few employees, it is part of Rails' DNA to seek ways to keep web development simple. This is why Rails is known as the [One Person Framework](https://world.hey.com/dhh/the-one-person-framework-711e6318).

Attending the latest edition of [Rails World](https://rubyonrails.org/world/2024) and watching the talk by [DHH](https://www.youtube.com/watch?v=iqXjGiQ_D-A) made me realize that generating views on the backend with Rails was no longer synonymous with slow, ugly interfaces that do not care about UX. With Hotwire, through Turbo and Stimulus, it was possible to create applications as complex as Gmail, [Hey](https://hey.com/), or Slack, [Campfire](https://once.com/campfire). And this became even more surreal with [Turbo 8](https://evilmartians.com/chronicles/the-future-of-full-stack-rails-turbo-morph-drive).

![SSR Giphy](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExYnprZ2x6bjdudzZ1Y2NqOXlqdGVwMzZvY2ZhOGFlbDFpazdpOWJ0ZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/vUvxwcgQ6Iqlm2p6g9/giphy.gif)

But the great benefit of returning to Server Side Rendering (SSR) is not needing APIs. Creating an API, whether REST or GraphQL, makes development slower. And it's not just Rails that is realizing this; Elixir with Liveview, PHP with [Livewire](https://livewire.laravel.com/docs/quickstart), and even JS with [HTMX](https://htmx.org/), and [React](https://react.dev/reference/react-dom/server) are following this trend. As Deno.js says: [The Future (and the Past) of the Web is Server Side Rendering](https://deno.com/blog/the-future-and-past-is-server-side-rendering).

## Components Are Here to Stay

As we saw in Part I of this article, views in React/Vue/etc. introduced the concept of components. These components are small interface blocks that condense "Style (Tailwind)", "Structure (HTML)", and "Behavior/JavaScript". Componentization, in addition to promoting reuse and facilitating testing, allows customization or use to create your own Design System. But most importantly, they are ready-made. You don't need to reinvent the wheel. A great example is [Shadcn](https://ui.shadcn.com/), launched only in March 2023 and already has 60k stars on Github.

Unfortunately, Rails views were created in a pre-components context. Html.erb is very good for writing HTML, but it is not good for writing behavior. Partials are great for writing behavior, but they are terrible for HTML.

To try to solve this, the first solution that emerged was [ViewComponents](https://viewcomponent.org/), a framework created by the folks at Github. ViewComponent is our React, allowing the frontend to be thought of as components. It is the most popular solution today. Although I have never used it, I feel it lacked boldness. It seems like a fusion of partials with erb. But what disappointed me the most was that [Github is migrating from Rails to React](https://news.ycombinator.com/item?id=33576722), and the maintainers of ViewComponents [don't seem very enthusiastic](https://www.youtube.com/watch?v=YdeuXQJkZrs).

As an alternative to ViewComponent, I found [Phlex](https://www.phlex.fun/). It comes with the boldness of creating components using only Ruby code. This is the kind of boldness that scares. I was scared when I first saw React put HTML, JS, and CSS (StyledComponents) inside a component. I was scared to see Tailwind create classes for each CSS property and have HTML created with dozens of classes in an element. Letting go of this "prejudice" and testing Phlex was important here. But the decisive factor was following the creator of Phlex, [Joel Drapper](https://x.com/joeldrapper), on social media and realizing how motivated he is.

## Component Library

As we do not like to reinvent the wheel here at Linkana, we decided to look for component libraries that we could use and evaluated:

- https://phlexui.com/
- https://primer.style/guides/rails
- https://shadcn.rails-components.com/
- https://railsdesigner.com/
- https://zestui.com/
- https://railsui.com/
- https://protos.inhouse.work/

We ended up choosing [Phlex UI](https://phlexui.com/), not because we believe it is ready, but because we believe the path it has chosen is the most suitable for the premise of reusable and easily customizable components. It is a library clearly inspired by [Shadcn](https://ui.shadcn.com/) and brought some Shadcn innovations to the Rails world.

1. Encouraging you to [customize](https://phlexui.com/docs/customizing_components) components by copying them into your codebase.

2. A component structure very similar to React:

Shadcn
```javascript
<Accordion type="single" collapsible className="w-full">
  <AccordionItem value="item-1">
    <AccordionTrigger>Is it accessible?</AccordionTrigger>
    <AccordionContent>
      Yes. It adheres to the WAI-ARIA design pattern.
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

PhlexUI
```ruby
render PhlexUI::Accordion.new do
  render PhlexUI::Accordion::Item.new do
    render PhlexUI::Accordion::Trigger.new do
      p { "What is PhlexUI?" }
    end
      
    render PhlexUI::Accordion::Content.new do
      p { "PhlexUI is a UI component library for Ruby devs who want to build better, faster." }
    end
  end
end
```

3. All CSS is written with Tailwind inside the component itself. It is very easy to make small adjustments/changes.

4. The components are made up of several very simple Ruby classes. It is very easy to understand code.

![Phlex UI Components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzn0y1wap4fq21dnl438.png)

5. Integration with Stimulus through specific controllers for each component.

## Putting it into Production

The first step to start the migration to SSR with Phlex and Hotwire was to map the PhlexUI components and place them in [Lookbook](https://lookbook.build/) within our application with our brand colors.

![Lookbook](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmnmmo5gbazzc3nvya30.png)

The second step was to create a Rails version of all navigation elements. Sidebar, tabs, etc.

This way, we managed to rewrite route by route using SSR while the React (SPA) routes continued to function. For example:

![Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kmwybxgx8bse27myzfp4.png)

Everything that is part of the Dashboard is already using SSR. Navigating between the "Summary" and "Lead time" tabs is a navigation that uses Hotwire's Turbo.

![Pendencies](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/krc5by32evcpda0v6jpt.png)

When the user clicks on the pending tab, we disable Turbo, and an HTML request is made to the backend that loads our SPA in React and displays the page. Thus, the application switches to SPA mode, and the requests return to being via our GraphQL API with JSON exchange.

If the user clicks on a link that is already in Hotwire, we make React perform an HTML request, and the application returns to being an SSR.

The transition from SPA to SSR is practically imperceptible, but from SSR to SPA is a bit slower, without hindering the user experience. Our idea is to complete this transition by the next edition of [Tropical.rb](https://www.tropicalrb.com/).

Since June 11th, we have been running Phlex in production, and as a form of gratitude, Linkana has become one of the [sponsors of the project](https://github.com/sponsors/joeldrapper#sponsors). We have also started contributing to the PhlexUI project.

If you are also working with components in Rails, leave a comment about what you have been doing.