Web Styling with ReactJS

I’m Ngo Xuan Bach from User Interface Technologies (UIT) team. We’ve worked on Charmmy and several services of LINE 占い (LINE Uranai, or LINE Fortune).
In this post, we will talk about how we implement web styling with ReactJS, the problems we’ve met, and how we propose to solve them.

0. Our problem when deciding on a styling method

The introduction of ReactJS has completely changed the way we build web applications. Just after a couple of years, ReactJS has become one of the most popular Front-End libraries, next to VueJS and Angular. Because React is platform-agnostic, there is no first-party support for styling in web platform, and it is up to you to decide which method to use.

Since each LINE product has a unique UI/UX thanks to the comprehensive design guidelines as part of its branding strategy, it is nearly impossible to rely on a single pre-built CSS framework or UI kit. While trying out different approaches to style our ReactJS websites, we keep an eye out for something that is:

  • Familiar and relatively easy to learn. Having out teammates to learn a completely new technologies might put a dent in our productivity.
  • Fully-featured. Existing features of CSS, including @keyframes, @media, etc. are must-have’s. Other features such as nested selectors and variables support are nice-to-have.
  • Inter-operable with JavaScript. It’s best if we can manipulate CSS with JS variables.
  • Strongly coupled. For better code maintainability, a stylesheet should only be used for one component and vice-versa.
  • Isolatable. There should never be a styling conflict between stylesheet among components. This cannot be stressed enough, especially in big teams.

There are styling methods in the wild that we will take a look at, and see what will be the most suitable for us. Is there a silver bullet? Let’s see.

1. Styling ReactJS website by writing your own CSS

This is the most common approach for a team to transit from traditional server-side rendered websites to client-side rendered website (SPA), since developers can re-use all the stylesheets without major changes. With this method, there’s no need to learn new fancy technologies, nor to rework all the stylesheets currently in place.

To have those stylesheets applied to your website, you have to either add it to the generated HTML, or include it in your React app entrypoint, depending on the scope of those stylesheets.

App.jsx

import styles from './App.module.scss';

const App = () => {
  return (
    <div className={styles.myApp}>
      Hello world
    </div>
  );
};

App.module.scss

.myApp {
  fontSize: 18px;
  color: #ffcccc;
  &:hover {
    color: #ff0000;
  }
}

In the above snippet I wrote styles for App component in a SCSS file called App.module.scss. The class name myApp written in SCSS file will be transformed internally by CSS Module, and then we can get the transformed class names via exported styles module.

This way, you can take advantage of the many existing CSS technologies and features, such as CSS preprocessors, post-processing features like autoprefixer, flexbugs-fixes, cssnext, etc. via PostCSS. Many IDEs and tools support code highlight, suggestions and linting to ensure you to only write good code.

When using separated CSS files, it is very common to write each CSS file next to the React component where it is used, and import it directly in that React component. Writing CSS this way will make the dependency between Component and its style clearer and more maintainable.

It is worth mentioning that using CSS file lets you utilize a feature called CSS module. CSS module will make your CSS files export an object of its original classnames, allow original classnames to be safely transformed to bring in classname isolation. This helps you avoid potential class names conflict and the inconvenient situation when a component accidentally uses classes from other CSS files altogether.

It is recommended that, once you go for CSS module, there’s no going back. You should stick to CSS module and avoid writing selectors like .container > div {} or .container i {}, because such selectors could unexpectedly affect other elements when this particular component of yours also renders other components.

2. Styling React websites using CSS utility frameworks

Utilising a CSS framework or library improves your efficiency and thus allows you to concentrate on building pages and writing application logic. Utility CSS frameworks like Tailwind, Tachyons and Atomic CSS have one thing in common: you only need to combine provided class names to get the desired UI.

One of our requirements is building pixel-perfect websites, and we found that using a CSS utility framework could considerably boost our productivity without jeopardizing this requirement.

At least one of our products have been built using Tailwind CSS recently. It is very promising, and we want to use it in our next products as the main method of styling web applications.

Despite its advantages, such CSS frameworks are not easy to learn, so it might take you some time to get started. One thing that might deter developers from using those CSS frameworks is that sometimes your code might be hard to understand, such as the following snippet.

Button.tsx

import classNames from 'classnames';

const Button = ({ variant = 'primary', disabled = false, children }) => (
  <button
    className={classNames(
      'rounded',
      disabled ? 'text-white bg-gray-400 cursor-not-allowed' : [
        'text-black cursor-pointer',
        variant === 'primary' && 'bg-teal-400',
        variant === 'secondary' && 'bg-blue-400'
      ],
    )}
  >
    {children}
  </button>
);

See? This kind of class name is quite difficult to understand and debug. Another reason is that you always need to play nice with specificity, otherwise it may come back to haunt you one day.

3. Styling React websites using CSS-in-JS

The above method of styling React website using CSS exposed many weaknesses, like the decoupling of components and stylesheets; the difficult to manipulate stylesheet with power of JavaScript.

With these difficulties, many CSS-in-JS library arised: StyledComponents, Styled JSX, emotion, JSS, etc.

CSS-in-JS is a new technology popularized by those who use React for web, but it’s not limited to just website. CSS-in-JS was defined as a way to write stylesheets right in JS or TS files, hence the name.

App.jsx

import { css, jsx } from "@emotion/react";
/** @jsx jsx */

const style = css`
  font-size: 18px;
  color: #ff8888;
  &:hover {
    color: #ff0000;
  }
`;

const App = () => {
  return (
    <div css={style}>
      Hello world
    </div>
  );
};

In this snippet I styled the App component with stylesheet provided by emotion. The stylesheet was written in a syntax similar to as if it’s written in a SCSS file.

There are many advantages to this method, but be prepared to learn a lot of new things. One of their top features is the isolation of stylesheet, allowing you to write stylesheet and component in the same file – and rest assured that the styles written for a component are only used on that component and not leaked anywhere. Modern CSS-in-JS libraries have covered most of CSS features like @media queries, pseudo selectors, @keyframes, nested selectors, among others.

Users of these libraries have their styles writen in object syntax or string literal syntax. Either method will have the same effects.

This method is unique in that it lets you craft your style with JS variables, thus enables you to apply theme to your React website. It’s only possible because all your stylesheets are generated during run-time – not during build-time like the above method. So all in all, this method will cause a bit extra overhead, but it won’t be noticable.

If your website is rendered on the server side, then it might require some configuration since these libraries might or might not work with SSR out-of-the-box.

Tip: In conjunction with CSS-in-JS libraries, you might want to take a look at polished. It’s a library which provides many ulitities and features useful for working with CSS and colors, dimensions, etc.

4. Our method

Initially we thought that with these difficulties we should write styles from scratch instead of finding a suitable CSS framework. However after many trials, we’ve found a solution most suitable for us so far. TailwindCSS has won us over to become our first choice of styling.

  • It lets us rapidly develop screens, pixel-perfect aligned. And in the best scenario, it might even enable us to develop a beautiful app without a single line of CSS.
  • Developing with Tailwind is fast. In fact, we have seen a big improvement in project productivity, because with Tailwind we don’t have to spend much time working on styles.
  • It is easy to learn and remember. Most of our members could get started and fall in love with it in no time.

Now there are a couple of things to keep in mind when working with Tailwind.

  • To make it work with our pixel-perfect designs, we had to heavily modify its configuration. A fairly easy task though.
  • We’ve also found out that Tailwind is not the answer to all problems. When that happens, we come back to traditional stylesheets with the help of styled-jsx, since it is already supported in NextJS that we’re using.

While using this stack for the recent projects, we have met no unsolvable problem – yet. Some may arise in the future, and we’re looking forward to tackle new challenges.

Having said that, the multiple methods of styling React web application might pose another problem altogether: the issue of decision making. But not at LINE! Here everyone is encouraged to propose and debate, but once a decision is made, the team stays together and make it better over time. We call it “Keep in Sync, Aiming for the Same Goal”, a part of LINE Style. This principle has guided us through many obstacles and built the team spirit.

Thanks for staying with me this far! If you find this post useful, please follow LINE Technology Vietnam on Facebook and LinkedIn for more updates on tech news and events.