Styling React Components with styled-components
HTML , CSS and JavaScript form the foundation of all client-side applications. Each of these languages handles a different responsibility. HTML markup semantically organizes content, CSS rules styles content and JavaScript code adds dynamic interactivity to the content. Traditionally, developers build basic client-side applications along this standard separation of concerns, which groups code solely by language (and implicitly, by file type): HTML for structure, CSS for presentation and JavaScript for behavior. However, siloing code this way comes with a caveat. For example, if you have a carousel on your homepage, and you decide to modify its appearance, then you may need to consult three separate files ( .html , .css and .js files) to make the appropriate changes. With modern JavaScript libraries/frameworks, such as React , the separation of concerns shifts from languages to components. Now, all code belonging to a single component lives within a single file ( .jsx / .tsx for React). The component itself, written as a functional or class component, contains methods (lifecycle, render, etc.) that encapsulate its behavior. The JSX syntax within the render method (or within the return statement if written as a function) describes the component's structure. As for the component's presentation, there are two common approaches: Large CSS stylesheets are difficult to maintain, especially when tracking which rules override others and which rules can/cannot be removed. If you write styles using a CSS-in-JS library, then all styles belonging to a component exists alongside the component within a .jsx / .tsx file (or .js file if imported from a separate file). When the component is no longer used within the application, then deleting the component's styles is as simple as deleting the component itself. For a React component library, CSS-in-JS libraries provide lots of functionality to supercharge components' styles: Below, I'm going to show you how to add styled-components to a React component library and how to style a component with styled-components . To understand how styled-components works, let's walkthrough a simple example: ( src/components/Heading.jsx ) ( src/App.jsx ) Note : By convention, the names of styled components are prefixed with Styled , which tells developers distinguish styled components from other components. Here, the <Heading /> component renders the styled component <StyledHeading /> , which is an <h1 /> element that is bolded, colored black and has a font size of 4rem . In fact, you can customize the styles based on props passed to the styled component: ( src/components/Heading.jsx ) ( src/App.jsx ) The most common way to write styled components is with tagged template literals . Appending a template literal to the end of a tag function name executes the tag function with the template literal and its placeholder expressions passed as arguments. Example : When the logTagFnArgs function is called, the template literal is broken up into smaller substrings based on the positions of the placeholder expressions. As you can see, the substrings that come before and after the placeholder expressions (including empty strings if the placeholder expression is located at the start/end of the template literal), such as "Sum of " before ${x} , are stored within an array that's passed as the first argument to logTagFnArgs . Each placeholder expression is passed as a separate argument to logTagFnArgs . With the rest parameter syntax, we can have the function accept these separate arguments as a single array, hence why the exps array contains the values 5 ( x ), 10 ( y ) and 15 ( x + y ). In the styled-components example, styled.h1 is a tag function. Knowing that styled.h1 returns a React component, we can write our own basic implementation of the styled.h1 tag function. First, establish a list of HTML tags you want the styled object to support. Let's start off with <h1 /> , <h2 /> , <h3 /> , <h4 /> , <h5 /> and <h6 /> tags. Then, place them within a styled object, which will be exported for other files to import. createStyledComponent will be a function that accepts a tag as an argument (since it must know which HTML element to render), and it will style and return a React component based on the tagged template literal. Due to the closure, the anonymous function returned by createStyledComponent allows the functional component to access the tagged template strings and expressions. Also, some of the expressions are functions that involve props passed to the functional component. Using the tagged template strings, expressions and props, the functional component takes these values and applies styles to the element accordingly. parseStyles parses the tagged template literal into an object that contains the CSS rules as key/value pairs. All function expressions are evaluated with the component's props passed as its argument. For example, if the component has no props passed to it, then this... becomes... And the element's style attribute is set to this object. All excess whitespace around the rules and their properties/values are removed. All non-integer and non-string values (booleans, functions, arrays, etc.) are omitted. Altogether... ( src/styled.js ) If we substitute styled-components with this implementation, then the application still works. ( src/components/Heading.jsx ) Nevertheless, this implementation only inlines styles directly on the element. It does not generate custom CSS class names and cannot process SCSS-like syntax with nesting, such as &:hover . styled-components uses the CSS preprocessor Stylis to compile code written with this syntax into CSS code. The purpose of this implementation is to demonstrate how styled-components works under-the-hood at a high level. Let's clone the repository react-d3-charts , a component library that contains customizable D3 visualizations written in React and TypeScript. Currently, this repository only has one visualization component, a scatterplot. Inside of the project directory, install the dependencies: Install the library styled-components as a dependency. Next, install @types/styled-components , which has TypeScript types for the styled-components library, as a dev. dependency. Inside of the src/components/XAxis.tsx file, which houses the component that renders a x-axis for the scatterplot component, observe that the two <text /> elements draw upon the same set of attributes and values: font family, font size and fill. If the component had many <text /> elements that rely on these same attributes, then there will be more repetitive JSX code. Let's place these styles within a single styled component named StyledText . Swap out the <text /> element with the <StyledText /> component and omit the fill , fontFamily and fontSize attributes. If you inspect this styled component via the developer console, then you will see a custom CSS class name added to the <text /> element. We can create styled components for the <path /> and <line /> elements. If you inspect these styled components via the developer console, then you will see custom CSS class names added to the <path /> and <line /> elements. If you check the contents of src/components/YAxis.tsx , then you will see that the y-axis has the same elements with the same styles as the x-axis: <path /> , <text /> and <line /> . We can refactor the code to make the styled components <StyledPath /> , <StyledText /> and <StyledLine /> reusable for the <YAxis /> component. Create a new folder src/styled and a new file src/styled/index.ts . Move all of the styled components into src/styled/index.ts and export them. ( src/styled/index.ts ) Inside of both src/components/XAxis.tsx and src/components/YAxis.tsx , substitute the <path /> , <text /> and <line /> elements with the styled components <StyledPath /> , <StyledText /> and <StyledLine /> exported from the src/styled/index.ts file. ( src/components/XAxis.tsx ) ( src/components/YAxis.tsx ) For a final version of this tutorial, check out the GitHub repository here . Add more styled components to this library! If you already maintain a React component library, then try adding styled components to your library.