TL;DR: Modern styling in React includes CSS Modules, Styled Components, and Tailwind CSS.
- CSS Modules enable local scope styling, preventing class name conflicts.
- Styled components offer CSS in JS, allowing dynamic styling with props.
- Tailwind CSS provides utility-first classes, speeding up the styling process.
Styling is an essential and integral part of modern web development. It has been the tagging partner of HTML since the inception of the browser, providing all the shine to HTML elements and webpages. It attracts users and encourage them to use a website for longer through a better user experience.
The way we write style has been evolving with modern JavaScript frameworks. In modern libraries like React, component-driven development provides abstraction at the unit level on the UI or webpage, where the business logic and state are isolated to the component itself. Styles have also evolved to be abstracted to the components themselves. These components can be extended or composed to create modules or other UI components of the webpage.
Long gone are the days when we struggled with global class names that could conflict with different HTML elements on other webpages, especially in the large codebase.
Modern JavaScript frameworks like React allow you to write styles at the component level. You can have the same class names for different components, but they won’t conflict. Kudos to modern bundling tools such as Webpack, which generate unique classes and styles to avoid conflict.
They also support code-splitting and tree-shaking styles, which allow us to load the styles specific to the components rather than everything at once, optimizing the page speed.
In this article, we are going to see multiple ways of styling specific to React:
CSS Modules
CSS Modules are defined by naming the CSS files *.modules.css, which allows you to access the CSS classes and animations defined in the stylesheet as JavaScript objects. You can assign them to the React elements.
app.module.css
.yellow {
color: yellow;
}
import Styles from "./app.module.css";
const App = () => {
return (
<>
<h1 className={Styles.yellow}>Hello World!</h1>
</>
);
};
export default App;
The classes and animations are scoped locally to the component, and React provides support for the CSS Modules under the hood, so you don’t have to make any configuration changes. You can import the CSS Modules file in your component and then use the import alias to access the CSS classes as objects.
The only problem with this is that you cannot merge to a class like you do while defining the normal CSS class in the string.
<h1 className="yellow large">Hello World!</h1>
This is because the CSS is now converted to ICSS, a low-level compilation of CSS that can be accessed as JavaScript objects.
To use multiple CSS classes together, we will need to use packages, such as classnames, that allow us to merge CSS styles accessed as objects.
app.module.css
.yellow {
color: red;
}
.large {
font-size: 3em;
}
import Styles from "./app.module.css";
import cx from "classnames";
const App = () => {
return (
<>
<h1 className={cx(Styles.yellow, Styles.large)}> Hello World!</h1>
</>
);
};
export default App;
Advantages of using CSS modules
- Scoped styles —The styles are locally scoped to the component, which avoids the conflict of the class names. Classes with the same name can have different styles that will be updated to unique random values during the build creation.
- Isolated dependencies —All the dependencies defined in the CSS Module are scoped to that file only and load when the component loads. This keeps the dependencies clear and scoped to that component.
- Boosts modularity —As the styles defined are locally scoped, it is easier to define the modular and reusable styles.
Styled Components
CSS Modules promote modularity, but we still have to maintain a separate style file to define the styles. Some developers find this better, as it provides a separation of concerns, as the business logic and styles are separate, while many prefer to have everything in a single file.
Styled Components allow us to write CSS in JavaScript, completely eliminating the need to maintain separate styling files. This keeps all three layers of a webpage in a single file: HTML, CSS, and JavaScript.
With Styled Components, we keep the styles isolated from the components in the true sense. It is a powerful way of styling the component that allows us to use React’s capabilities in the complete sense that the Styled Components can be used as a React component.
Let’s see some practical examples of the Styled Components to understand them better.
Installation
# npm
npm install styled-components
# yarn
yarn add styled-components
ES6 has introduced tagged template literals, in which you can tag a string literal to the function accessed inside it. Styled Components use them to define their styles.
import Styled from "styled-components";
const Button = Styled.button`
background: transparent;
border-radius: 3px;
color: black;
border: 1px solid;
display: inline-block;
margin: 0.5em 1rem;
padding: 0.5em 0;
transition: color .25s ease;
cursor: pointer;
`;
With Styled Components, you can define the React component itself rather than applying the CSS style to the HTML element explicitly.
The variable Button can be used as the React component, whereas the object accessed from the Styled.button defines the HTML element that will be created, which in this case is the button.
const Test = () => {
return (
<>
<Button>Click me</Button>
</>
);
};
export default Test;
The style is directly applied to the HTML element, and we can apply all sorts of styles as we do in the CSS, even on pseudo-states.
const Button = Styled.button`
...
&:hover{
color: red;
}
`;
As styled components create React elements, we can pass props to them and access them. They can be used to apply dynamic styles.
import Styled from "styled-components";
const Button = Styled.button`
background: ${(props) => (props.filled ? "gray" : "transparent")};
border-radius: 3px;
color: ${(props) => (props.filled ? "white" : "black")};
border: 1px solid;
display: inline-block;
margin: 0.5em 1em;
padding: 0.5em 0;
transition: color .25s ease;
cursor: pointer;
&:hover{
color: ${(props) => (props.filled ? "blue" : "red")};
}
`;
const Test = () => {
return (
<>
<Button filled>Click me</Button>
</>
);
};
export default Test;
This promotes the container/presentational pattern of the development in React, where the component does not maintain its state, but changes based on the props it receives.
This helps to create a design system with the Styled Components, where the designers can create basic sets of common components that React developers can use to create other components.
This is also possible because it is easier to extend the style of the HTML elements or elements created using Styled Components.
import Styled from "styled-components";
const Button = Styled.button`
background: ${(props) => (props.filled ? "gray" : "transparent")};
border-radius: 3px;
color: ${(props) => (props.filled ? "white" : "black")};
border: 1px solid;
display: inline-block;
margin: 0.5em 1em;
padding: 0.5em 0;
transition: color .25s ease;
width: 10em;
cursor: pointer;
&:hover{
color: ${(props) => (props.filled ? "blue" : "red")};
}
`;
const GreenButton = Styled(OutlinedButton)`
background: green;
border-color: yellow;
`;
const Test = () => {
return (
<>
<Button filled>Click me</Button>
<GreenButton filled>I am Green</GreenButton>
</>
);
};
export default Test;
When we extend the styles, it overwrites the existing ones and applies the new ones.
The props are passed down in the hierarchy automatically and can be accessed in both the extended component and the base component. We can also compose the Styled Components as normal React components.
import Styled from "styled-components";
const Wrapper = Styled.main`
padding: 10px;
max-width: 1100px;
margin: 0 auto;
`;
const HeroArea = Styled.section`
background: green;
height: 65vh;
`;
const Test = () => {
return (
<Wrapper>
<HeroArea>Hello World!</HeroArea>
</Wrapper>
);
};
export default Test;
Advantages of using Styled Components
- Better developer experience —With Styled Components, developers feel closer to writing React code than HTML and CSS separately.
- Better modularity and abstraction —As the styles are isolated to the components themselves, they are better scoped to the component, providing style abstraction, which could avoid CSS conflicts.
- Better performances —As there are no separate files, the Styled Components are tightly bound to the React component. This way, they support code-splitting, and the Styled Components keep track of the component rendered to the page and inject the styles that belong to that component.
- Backward compatibility —With Styled Components, you write modern CSS, and it handles everything else under the hood.
Tailwind CSS
Many popular CSS frameworks emerged before Tailwind, including Bootstrap and Foundation. These frameworks provided a pre-defined set of components and styles that we had to use as they stated.
Tailwind CSS has tried to shift away from traditional methods of writing styles by providing a set of low-level utility classes that you can use to create your custom designs, similar to Lego blocks. This makes it a utility-first CSS framework.
Key features of Tailwind CSS
- Utility-first approach— Tailwind encourages us to approach styling from a different perspective. Instead of writing custom styles for each component, you apply utility classes directly in your HTML. This makes it easier to see how elements are styled at a glance.
- Customization— Tailwind CSS is highly customizable. We can configure it to generate only the classes we need, which reduces the size of the CSS files. We can also extend the framework with our custom utility classes and themes.
- Responsive design— Tailwind CSS makes it simple to create responsive designs with its built-in responsive utilities. Using intuitive class names, we can easily apply different styles at different breakpoints.
- Productivity— By using predefined utility classes, we can significantly speed up our development process. Tailwind CSS reduces the amount of custom CSS we need to write, which means fewer context switches between HTML and CSS files.
Initializing Tailwind CSS
To start using Tailwind CSS in your React project, you need to initialize it. This involves installing Tailwind CSS and its dependencies and setting up the necessary configuration files.
Install Tailwind CSS and dependencies:
In your project directory, run the following command to install Tailwind CSS along with PostCSS and Autoprefixer.
npm install tailwindcss postcss autoprefixer
Initialize Tailwind CSS:
Use the following command to generate the Tailwind CSS configuration file (tailwind.config.js) and the PostCSS configuration file (postcss.config.js).
npx tailwindcss init -p
Adding the -p plag ensures that the files tailwind.config.js and postcss.config.js are created.
Setting up Tailwind CSS configuration file
The tailwind.config.js file allows you to customize your Tailwind CSS setup. You can extend the default configuration, add custom themes, and configure other settings.
Configure purge option
To optimize your CSS file size for production, configure the purge option. This option removes any unused CSS classes from your final build. Open tailwind.config.js and update the content property.
module.exports = {
purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
This configuration ensures that Tailwind CSS scans all JavaScript, TypeScript, and HTML files for class names in the src and public directories.
Customize theme
You can extend the default theme by adding custom colors, fonts, spacing, and more. For example, to add a custom color, modify the theme property.
module.exports = {
purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
colors: {
primary: "#1DA1F2",
secondary: "#14171A",
},
},
},
variants: {
extend: {},
},
plugins: [],
};
The above example adds two custom colors: primary and secondary.
Add plugins
Tailwind CSS can be extended with plugins to add additional functionality. For instance, you can add forms or typography plugins. Install the plugin and add it to the plugin array.
const forms = require("@tailwindcss/forms");
module.exports = {
purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [forms],
};
In this example, the @tailwindcss/forms plugin is added to enhance the styling of form elements.
Integrating Tailwind CSS with Create React App project
Now that you have Tailwind CSS installed and configured, the next step is to integrate it with your Create React App project.
Add Tailwind directives to your CSS
Create a CSS file if it doesn’t already exist (e.g., src/index.css ) and add the following Tailwind CSS directives.
@tailwind base;
@tailwind components;
@tailwind utilities;
These directives import Tailwind’s base styles, components, and utility classes into your CSS.
Import the CSS file into your React project
Open src/index.js (or src/index.tsx if you’re using TypeScript) and import the CSS file at the top.
import './index.css';
Verify Tailwind CSS integration
Start your development server to ensure everything is configured correctly.
npm start
You can now start using Tailwind CSS classes in your React components. For example, open src/App.js and modify it to include some Tailwind CSS classes.
function App() {
return (
<div className="App">
<header className="bg-blue-500 text-white p-4">
<h1 className="text-3xl">Welcome to My Tailwind CSS React App</h1>
</header>
<main className="p-4">
<p className="text-gray-700">
This is a simple example of using Tailwind CSS with React.{" "}
</p>
</main>
</div>
);
}
export default App;
Check for applied styles
Open your browser and navigate to http://localhost:3000 to see your React app styled with Tailwind CSS. You should see a header with a blue background and white text, and a paragraph with gray text.
Understanding Tailwind CSS basics
Utility-first approach
The utility-first approach in Tailwind CSS is a fundamental shift from traditional CSS methodologies. Tailwind has a set of low-level utility classes that we can directly assign to the HTML elements to create different designs.
Key benefits
- Speed: Rapidly build and iterate on designs without context-switching between HTML and CSS files.
- Consistency: Apply consistent styling across your app using pre-defined classes.
- Readability: Easily understand the styles applied to an element by looking at its class names.
Example
Instead of writing the following custom CSS:
<div class="my-component">
<h1 class="title">Hello World</h1>
<p class="description">Welcome to my website.</p>
</div>
<style>
.my-component {
padding: 16px;
background-color: #f8f8f8;
}
.title {
font-size: 24px;
color: #333;
}
.description {
font-size: 16px;
color: #666;
}
</style>
You use Tailwind utility classes, like in the following code.
<div class="p-4 bg-gray-100">
<h1 class="text-2xl text-gray-900">Hello World</h1>
<p class="text-lg text-gray-600">Welcome to my website.</p>
</div>
Tailwind CSS classes and directives
Tailwind CSS provides a comprehensive set of utility classes and directives to style your HTML elements. These classes are categorized into several groups:
Layout
container //Centers the content with responsive padding.
box-border, .box-content //Sets the box-sizing property.
block, inline-block, inline //Controls element display type.
Example
<div class="container mx-auto">
<div class="block bg-white p-4">Content</div>
</div>
Flexbox and grid
flex, inline-flex //Enables flexbox on an element.
flex-row, flex-col //Sets the flex direction.
grid, inline-grid //Enables grid layout on an element.
Example
<div class="flex flex-col items-center">
<div class="flex-1 bg-blue-500">Item 1</div>
<div class="flex-1 bg-green-500">Item 2</div>
</div>
Spacing
m-4, p-4 //Sets margin and padding.
mx-auto //Horizontally centers an element with auto margins.
space-x-4, space-y-4 //Adds space between child elements.
Example
<div class="p-4">
<div class="m-4 bg-red-500">Content</div>
</div>
Sizing
w-1/2, h-1/4 //Sets width and height as fractions.
w-full, h-screen //Sets width and height to full size.
Example
<div class="w-1/2 h-1/4 bg-yellow-500">Content</div>
Typography
text-lg, text-2xl //Sets font size.
font-bold, italic //Sets font weight and style.
text-center, text-left //Sets text alignment.
Example
<p class="text-lg font-bold text-center">Hello World</p>
Background and border
bg-blue-500, bg-red-200 //Sets background color.
border, border-2 //Sets border width.
rounded, rounded-lg //Sets border radius.
Example
<div class="bg-blue-500 border border-2 border-red-500 rounded-lg">Content</div >
Responsive design with Tailwind CSS
Tailwind CSS includes built-in responsive design utilities that allow you to apply styles at specific breakpoints. The default breakpoints are:
sm (640px)
md (768px)
lg (1024px)
xl (1280px)
2xl (1536px)
Example
To apply a utility class at a specific breakpoint, prefix it with the breakpoint name followed by a colon.
<div class="bg-red-500 sm:bg-green-500 md:bg-blue-500 lg:bg-yellow-500 xl:bg-pu rple-500">
Responsive Content
</div>
In this example, the background color changes at different screen widths.
Responsive flexbox
<div class="flex flex-col md:flex-row">
<div class="flex-1 bg-red-500">Item 1</div>
<div class="flex-1 bg-green-500">Item 2</div>
</div>
This layout stacks the items vertically on small screens and arranges them in a row on medium and larger screens.
Customizing Tailwind CSS
Tailwind CSS is highly customizable. You can modify the default configuration to fit your design requirements. Customizations are included in the tailwind.config.js file.
Extending the theme
You can add custom colors, spacing, fonts, and more.
module.exports = {
theme: {
extend: {
colors: {
primary: "#1DA1F2",
secondary: "#14171A",
},
spacing: {
"128": "32rem",
},
fontFamily: {
sans: ["Graphik", "sans-serif"],
},
},
},
variants: {},
plugins: [],
};
Customizing breakpoints
You can define your breakpoints if the defaults don’t fit your needs.
module.exports = {
theme: {
screens: {
xs: "480px",
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
},
},
};
Adding plugins
Plugins are used to add additional functionalities to Tailwind CSS. For instance, you can add a forms plugin to style form elements.
const forms = require('@tailwindcss/forms');
module.exports = {
theme: {
extend: {},
},
plugins: [forms],
};
Using @apply directive
The @apply directive allows you to compose utility classes in your CSS files.
.btn {
@apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
}
In this example, you create a .btn class that applies several utility classes simultaneously.
Creating components with Tailwind CSS: Building a navigation bar
A navigation bar is a crucial component of many web apps, allowing users to navigate through different sections of the site. With Tailwind CSS, you can create a stylish and responsive navigation bar easily.
<nav class="bg-white shadow-md">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<a href="#" class="flex-shrink-0 flex items-center">
<img class="h-8 w-8" src="logo.svg" alt="Logo">
</a>
<div class="hidden sm:ml-6 sm:flex sm:space-x-8">
<a href="#" class="text-gray-900 inline-flex items-center px-1 pt-1 b order-b-2 border-indigo-500 text-sm font-medium">Home</a>
<a href="#" class="text-gray-500 hover:text-gray-700 inline-flex item s-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium">About</a>
<a href="#" class="text-gray-500 hover:text-gray-700 inline-flex item s-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium">Services </a>
<a href="#" class="text-gray-500 hover:text-gray-700 inline-flex item s-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium">Contact</ a>
</div>
</div>
</div>
</div>
</nav>
Explanation
- bg-white shadow-md: Sets the background color to white and adds a shadow.
- max-w-7xl mx-auto px-4 sm:px-6 lg:px-8: Centers the content and sets padding for different screen sizes.
- flex justify-between h-16: Uses flexbox to space out the items and set the height.
- hidden sm:flex sm:space-x-8: Hides the links on small screens and spaces them out on larger screens.
- border-b-2 border-indigo-500: Adds a bottom border to the active link.
Benefits of using Tailwind CSS with React
Combining Tailwind CSS with React can significantly enhance your web app development experience and performance. Following are some of the key benefits.
Faster development:
- Utility classes: Tailwind’s utility-first approach allows you to quickly apply styles without leaving your HTML or JSX files. This reduces the time spent writing custom CSS and switching between files.
- Component reusability: React’s component-based architecture works seamlessly with Tailwind’s utility classes. You can create reusable components styled with Tailwind throughout your app, ensuring consistency and reducing redundancy.
Improved maintainability:
- Readability: With Tailwind CSS, you can see all the styles applied to an element directly in the JSX file. This makes it easier to understand and modify styles, leading to better codebase maintainability.
- Scoped styles: React components naturally encapsulate their styles, reducing the risk of style conflicts. Tailwind’s utility classes enhance this by keeping styles modular and scoped to specific elements.
Enhanced customization:
- Theming: Tailwind CSS allows you to define custom themes and extend its default configuration. This flexibility makes it easy to create a unique look and feel for your app.
- Responsive design: Tailwind’s defined utilities are responsive, which makes it straightforward to build responsive UIs. Combined with React’s component reusability, you can create responsive components that adapt to different screen sizes with minimal effort.
Performance optimization:
- Optimized CSS: Tailwind CSS includes a built-in tool called PurgeCSS, which removes unused CSS classes from your production builds. This keeps your CSS file size small and improves load times.
- Efficient rendering: React’s virtual DOM and efficient diffing algorithm ensure that only the necessary components are re-rendered when the state changes, leading to faster and more efficient updates.
Conclusion
Thanks for reading this article! The evolution of styling in web development with tools like CSS Modules, Styled Components, and Tailwind CSS has greatly enriched the developer’s toolkit. Each of these methods brings its own strengths to the table. By understanding and applying these techniques effectively, developers can ensure that their web apps are not only visually appealing but also maintainable and scalable.
The choices in styling methods can profoundly impact the development workflow and the final user experience. It’s worth investing the time to explore these options and integrate them into your web development practices to achieve optimized, conflict-free, and engaging web apps.
The Syncfusion React UI components library is the only suite you will ever need to build an app. It contains over 85 high-performance, lightweight, modular, and responsive UI components in a single package.
The newest version of Essential Studio is available on the license and downloads page for existing Syncfusion customers. If you are not yet a customer, try our 30-day free trial to test the latest features.
You can always contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!