Curious about CSS in React? or React-Powered Solutions? As a front-end developer, you know the value of CSS in web development, but how does it work with React? Join me as we will be covering everything from the fundamentals to more advanced techniques.
Vanilla CSS
Like most, I began my web development journey with vanilla CSS - a simple approach that only requires a single HTML and CSS file.
As I created bigger applications, I gradually realized some of its drawbacks.
- Lack of reusability: It's easy to create overly complicated stylesheets that are difficult to reuse.
- Global Namespace. Since CSS has a global namespace, it can accidentally target unintended elements.
- Co-location. Modularity is difficult to achieve, making it challenging to safely remove unused code.
I was later introduced to CSS pre-processors, which are tools designed to add new functionality to CSS and compile it back to vanilla CSS.
CSS pre-processors
I've likely used every CSS preprocessors out there in building design systems, including Sass/Scss, Sass-on-demand, Less, Stylus, PostCSS, and Stylecow. Each has slightly different syntax. PostCSS and Stylecow provide modern features and improved syntax. As a developer, it's important to choose the one that suits your project's needs and preferences. Now, let's dive into Sass.
Sass
Sass, a widely-used preprocessor, offers a way to write more reusable and maintainable CSS. It comes with several features, such as variables, mixins, and modules, that are very popular among developers.
- Variables. This feature enables me to define global values just once and use them throughout my stylesheets.
- Mixins. With this feature, I can easily reuse specific snippets of CSS without having to write them again and again.
- Modules. This feature helps me modularize my CSS files, making it easier to manage and delete code safely.
As I used Sass to write more modular and reusable CSS, I discovered some drawbacks over time:
- Naming. As the CSS codebase grows, it can be challenging to maintain naming conventions, and class names can quickly become unwieldy.
- Browser support. Some features of CSS are not supported across all browsers and require the use of vendor prefixes like
-moz-
(Firefox),-webkit-
(Safari), and-ms-
(Internet Explorer and Edge) for cross-browser compatibility.
To address naming challenges, I adopted the Block Element Modifier (BEM) methodology. BEM helps create reusable components and organizes CSS in a modular way, keeping specificity low.
While naming conventions helped with CSS naming, it didn't fully address the root cause. To handle vendor prefixes, it's possible to set up your Sass toolchain with a tool like autoprefixer. However, when I started using component-based frameworks like React, I delved into exploring CSS-in-JS.
CSS-in-JS
I delved into CSS-in-JS while building a component library, and it seemed to solve all my problems initially.
- Specificity was resolved through auto-generated class names.
- Colocation was addressed by placing CSS directly with the component, making it easy to remove code.
- Browser support was tackled by having autoprefixer integrated.
- Variables were resolved by establishing a global theme.
Despite the numerous advantages, I still encountered difficulties with naming.
As the component library grew, I realized the need for a more structured approach to scaling CSS-in-JS and enforcing a design system. That's when I came across Theme UI and styled-system, which not only helped me with scaling but also solved the issue of naming.
When I attempted to scale styled-components alone in a large application, I had to resort to creative naming approaches, such as using words like container
, wrapper
, or layout
, which quickly became unmanageable. For example:
Now, I know what you're thinking. Inline styles 🤮. At first, I was hesitant too. But eventually, it won me over - and that's where I am today.
Present State
Lately, I've been exploring CSS Modules and Tailwind CSS to style my React apps, leading me to take a closer look at the ecosystem.
Here's a breakdown of the pros and cons of different approaches to styling your React application. I've aimed to keep this comparison as impartial as possible. Your preferred option depends on:
- Your familiarity (stick with what you're comfortable).
- Your team.
- The size of your application.
- Your project requirements.
Pros and Cons
Vanilla CSS
CSS Modules
CSS Modules can be a suitable choice for smaller teams that do not need to share components across applications.
Next.js has built-in support for CSS Modules using the .module.css
extension, removing the need for Webpack configuring. Additionally, it comes with autoprefixer pre-installed, making CSS Modules a compelling option compared to CSS-in-JS in certain scenarios.
CSS-in-JS
There are various libraries available for CSS-in-JS. The most popular ones are styled-components and Emotion.
Some libraries like Linaria extract CSS to files at build-time, making it a zero-runtime solution. You can find CSS-in-JS benchmarks. to compare various libraries.
Theme UI / Styled System
Both Theme UI and styled-system are built around the System UI theme specification, which aims to make UI components, libraries, and tools as interoperable as possible.
These libraries are primarily used for implementing component libraries and design systems, with Chakra UI being one of the most popular examples.
Tailwind CSS
Tailwind CSS has been gaining popularity due to its minimalist approach in contrast to more bloated CSS frameworks like Bootstrap. It also offers an intuitive API that streamlines development.
When combined with Tailwind UI, it is an excellent tool to create scalable and aesthetically pleasing applications.
CSS in React Native and Expo
React Native is a popular choice for cross-platform mobile app development. Expo builds on top of React Native, providing additional tools and services. Styling is done with JavaScript objects instead of CSS.
CSS can be used through a styling system similar to inline styles in React. However, this approach has limitations, including the lack of support for pseudo-classes
and pseudo-elements
. Nativewind is a library that simplifies styling with a Tailwind-inspired set of utility classes for React Native components. I truly believe that the future of software developmet is in cross-platform development, in React Native, in Expo.
Conclusion
I hope that this comprehensive guide has provided you with a better understanding of the various approaches to styling React applications. By weighing the pros and cons of each method, you can determine which solution best fits your team, application size, and project goals. Remember, there is no one-size-fits-all solution when it comes to styling, and it's essential to consider factors like maintainability, scalability, and team collaboration. If you have any suggestions or feedback, please feel free to leave a comment below or reach out to me on Twitter.
Thanks for reading!