Styled-components is a wonderful way to style the components in your React project, but using it together with existing CSS can be a bit tricky – especially if you want the CSS styles to override the styled-components’ styles.
The suggested way in the styled-components docs is to increase the specificity of your CSS selectors. Let’s say we have a CSS class named “Header”, then their suggestion is to do this:
/* CSS properties here */
This solution wouldn’t do the trick in our situation. The CSS files that we’re going to use will be written by someone else and we don’t want to instruct them to bump the specificity of every single selector this way. It’s a hacky solution, in my opinion.
The issue with styles from styled-components overriding styles from CSS files derives from the order of the styles in the <head> of the index.html document. The bundler, Webpack in our case, will inject the styles from the CSS files in the React project to the end of the <head> during buildtime, while styled-components will do the same thing later during runtime. It will look something like this:
<style type="text/css”>…</style> // CSS from the CSS-files
<style data-styled="" data-styled-version=“4.2.0”>…</style> //CSS from styled-components
This means that the styled-components’ stylesheet will override any styles loaded before it. So how do we beat styled-components in specificity without having to write CSS selectors twice? We mimic their behavior! Let’s wait for styled-components to inject their styles into the <head> and then inject the link to the external CSS one row below.
Injecting a link to the external CSS
The entire example application can be explored here, but let’s have a closer look at index.js, the entry point to our example React application:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
// Attach the link to the merchant's CSS to the <head>
var externalCssLink = document.createElement("link");
ReactDOM.render(<App />, document.getElementById("root"));
Starting on line 8, a link element with rel, type and href attributes is created. In this example, we’ll use the Tachyons CSS framework as the external CSS. On line 15, the link element is appended to the end of the <head>. But how can we be sure that the styled-components styles aren’t injected and placed on a row below this link element?
This example application has one main component called App, which is imported on line 4 above and rendered with ReactDOM.render() on line 17. In App.js, three styled-components are used (Container, Header and BodyText) and imported from styledcomponents.js, where the components styled with styled-components are defined and the styled-components library is imported.
Because App is statically imported and App itself statically imports the styled Container, Header and BodyText components (as with all other imports in this example), the code in App.js and its subtree of imports will be executed before we inject the link to the external CSS in index.js. Thus, the styled-components library will already have injected its styles to the <head> and we can be sure that the external CSS link is injected below it and will have the highest specificity.
Here’s a link to the example application once again. In App.js on line 8, the component BodyText (which is styled with styled-components) has the className strike, which is a CSS class from Tachyons. In styledcomponents.js BodyText has set text-decoration: none, while the strike class has set text-decoration: line-through (from Tachyons docs). As you can see on the rendered webpage, the body text has strikethrough applied to it.