STYLED COMPONENTS


Web Development

Updated Feb 24th, 2022

The official site is at styled-components.com and a link to the docs is here. This is the “go-to” way to style your React components.

Note: One thing I don’t get is what the difference between theming and basic CSS variables.

Harry Wolf, Adrian Twarog, Fidalgo, and Academind.

npm install styled-components
and then
import {} from 'styled-components'

I love the idea that it cuts down on the number of files in your project. This assumes you keep your styles in the comp files. Which is definitely not always the case.

VS-Code-styled-components extension by Julien Poissonnier provides Intellisense and syntax highlighting.

Styled components is a library that helps you style your application both web and mobile. It is a form of CSS-in-JavaScript. It comes with a number of benefits such as access to props, easy named components, CSS syntax even in react native, and it’s available for React web and React native.

The goal of styled-components are to enhance styling for React, optimized development, no class name bugs, unique naming, simple dynamic styling, easier maintenance, and automatic vendor prefixing.

Features of styled-components include: Having dark/light mode is very easy to implement, easy styling for React Native, avoids conflicts with third-party styling, server side rendering ready, TypeScript ready, you can extend existing styled-components and there’s browsers support, everything pretty much and works with React native.

It’s used by some of the largest companies, Google, Vimeo, Airbnb, reddit. React web and React native have similar code structure.

With NextJS

Using the folder-structure strategy from JS Mastery

In the “_app.js” file there’s global style, “styles/globals.tsx,” and theme “styles/theme.tsx” imported. I think they separate this to prevent crowding in the app.js file

The styles/theme.tsx file imports in the theme/default.tsx file and the global styles file.

There is also a “styles/themes/default.js” file for fonts colors and breakpoints. What is weird is that this is “export default” only with no value and couldn’t get this to work since the error” cannot fins modules or type declarations” SO i worked those into the “theme.tsx” file” instead of living in its own file.

And for global components (sections/buttons, etc.) we have a “Styles/globalcomponents/index.tsx” file

_app.tsx
  styles/theme.tsx
    styles/themes/default.tsx
    styles/globals.tsx

Back To It

Creating a Styled Component

const Button = styled.button`
  css styles here
`

Note: Don’t put this inside of your component’s function as this is bad practice.

Passing Props to Dynamically Change Styling:

// in comp definition
background: ${(props) => (props.primary ? 'crimson' : 'teal') }
// in JSX
<Button primary></Button>

A cool example is using the flex property of justify-content

const PaginationWrapper = styled.div`
  display: flex;
  width: 100%;
  justify-content: ${({page}) => {
    if (page === 'first') return 'flex-end';
    else if (page === 'middle') return 'space-between';
    else return 'flex-start'
  }}
`
// in the JSX
<PaginationWrapper page="first"></PaginationWrapper>

I also saw passing props to a button styled-component depending on whether a quiz answer was correct or not using a nested ternary operator.

Note about using with Typescript and NextJS. Just passing props to a styled component will not work. You must first define the props as a custom type and then use it when defining the styled-component. Notice the question mark to make this optional so it is not mandatory to pass a dark prop every time you want to use the component. This is a good way to conditionally change a CSS property, while reducing duplicate code and staying TS compliant.

type SectionTitleProps = {
  correct?: boolean

}

export const SectionTitle = styled.h2<SectionTitleProps>`
  color: ${props => (props.dark ? "black" : "white")};
`
// and then in JSX
 <SectionTitle dark={false}>Title Here</SectionTitle>

Using State to Dynamically Change Styling:

Can use the “useState” hook

const [primary, setPrimary] = useState(false)

Down in the JSX you can pass a primary prop set to primary and brackets and maybe have an “onClick” prop with an arrow function that calls “setPrimary” to the opposite of what it currently is using with “!primary.”

You can also add some animation. You can also do hover states and media queries as well.

Can Target Classes

Can Do Animations

import styled {keyframes} from 's-c'

This is pretty sweet

Extending Styled Components

lets say you brought something in from material-ui

const SuperButton = styled(StyleButton)`
  font-size: 2.5rem;
`

Style Any Custom Existing Components: Override a couple properties without rewriting entire styled component because sometimes style is predefined.

const learnMoreReact = styled(existingComponent)`
  color: red;
`

Dark Mode: Create a theme for your application. Passed colors in his props and then change them on the fly such as if you want to go from light mode to dark mode.

import ThemeProvider from styled components.

import styled, {ThemeProvider} from 'styled-components'
const theme = {
  primary: 'orangered'
}

Down your JSX you can wrap your components in a “ThemeProvider” component. Create a theme and set it to an object primary and then put some base colors. Now you can pass your theme name as a prop.

Can Use Nested Psuedo Classes

&:hover{
  color: blue;
}

I’m not sure where I was having issues targeting pseudo elements but this is easy-peasy with styled-components.

Note: It’s inline-styles where this doesn’t work.

Interpolate Nested Stuff with css helper

import styled, {css} from "s-c"
${({primary}) => primary && css`
  border: 3px solid red;
`}

This is if you want to make a lot of changes based on one prop.

Theming

Theming is great and is similar to CSS variables in that you can change in one place and automaticlaly update in many places in your code. A use case (besides the typical colors) are “spacers” that define things like margin-top and paddings.

import styled, {ThemeProvider} from 'styled-components'
const theme = {
  primary: 'orangered'
}

Note: styles defined in the theme are in a JavaScript object and so the values must be wrapped in quotation marks.

Need to wrap your entire app in the “ThemeProvider” for all of your other components to have access to the theme.

function App() {
  return (
    <ThemeProvider theme={theme}>
      ...other components
    <ThemeProvider>
  )
}

Reference in you components

background: ${props => props.theme.primary}

Can have multiple theme providers if you want.

Add Multiple Properties

import styled {ThemeProvider, css} from styled-components
${(props) => props.color && css`
  background: ${(props) => props.theme[props.color]}
`}

Media Queries in SC

This is crucial for me because I’ve grown to love using “mixins” in SASS for media queries and not having a similar implementation in Styled-Components would be a deal breaker.

A good article here which shows a simple enough implementation but your still writing @media screen and blank all over the code just referencing variables in case you want to change them in the future.

Media queries are a great use case for themes. Typically you will see a “src/styles/themes/default.js” file with something like the code below. Notice their is no name for the object just “export default.”

export default {
  
  // Temp fonts
  fonts: {
    title: "Space Grotesk, sans-serif",
    main: "Space Grotesk, sans-serif"
  },
  
  // Colors for layout
  colors: {
    primary1: "hsl(204,23.8%,95.9%)",
    background1: "#0F1624",
    accent1: "hsl(34.9,98.6%,72.9%)",
    button: "hsl(205.1,100%,36.1%)",
    background2: "hsl(232.7,27.3%,23.7%)",
  },
  
  // Breakpoints for responsive design
  breakpoints: {
    sm: 'screen and (max-width: 640px)',
    md: 'screen and (max-width: 768px)',
    lg: 'screen and (max-width: 1024px)',
    xl: 'screen and (max-width: 1280px)'
  },
}

And then you reference the breakpoints in your code like this:

@media ${props => props.theme.breakpoints.sm} {
    //...CSS Here
  }

compared to @include atMedium {//...CSS Here}

But there is another “theme.js” file in “src/styles”

import { ThemeProvider } from 'styled-components';
import theme from "../themes/default";
import GlobalStyles from './globals';
const Theme = ({ children }) => (
  <ThemeProvider theme={theme}>
    <GlobalStyles />
    {children}
  </ThemeProvider>
);
export default Theme;

Can Inject Global Styles

And we have a “global.js” file in the “src/styles/” folder as well. Good place to put CSS resets and normalize files.

import { createGlobalStyle } from "styled-components"
import { normalize } from "styled-normalize"
const GlobalStyles = createGlobalStyle`
  ${normalize};
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }
  html {
    font-size: 62.5%;
    scroll-behavior: smooth;
  }
  body {
    font-family: ${props => props.theme.fonts.main};
    font-size: 1.6rem;
    background: ${props => props.theme.colors.background1};
    color: ${props => props.theme.colors.primary1};
    cursor: default;
  }
  h1,h2,h3,h4,h5,h6,button {
    font-family: ${props => props.theme.fonts.title};
  }
  
  a {
    text-decoration: none;
  }
  li{
    list-style: none;
  }
`
export default GlobalStyles

Put In Their Own File (Optional)

Should have some global components that go in “src/styles/GlobalComponents” and component-specific.

For the global

import styled from "styled-components"
export const Section = styled.section`
  display: ${props => (props.grid ? "grid" : "flex")};
  flex-direction: ${props => (props.row ? "row" : "column")};
  padding: ${props => (props.nopadding ? "0" : "32px 48px 0")};
  margin: 0 auto;
  max-width: 1040px;
  box-sizing: content-box;
  position: relative;
  overflow: hidden;
  grid-template-columns: 1fr 1fr;
  @media ${props => props.theme.breakpoints.md} {
    padding: 24px 48px 0;
    flex-direction: column;
  }
  @media ${props => props.theme.breakpoints.sm} {
    padding: ${props => (props.nopadding ? "0" : "16px 16px 0")};
    width: calc(100vw - 32px);
    flex-direction: column;
  }
`

and then reference

import { Section, SectionText, SectionTitle } from '../../styles/GlobalComponents';
import Button from '../../styles/GlobalComponents/Button';

For the component-specific add a sibling file in which you can have multiple styled-components exported (don’t need to have a default export).

export const DropDownIcon = styled.div`
  width: 32px;
  height: 32px;
  margin-right: 16px;
`
export const DropDownTextContainer = styled.div`
  display: flex;
  flex-direction: column;
`
export const DropDownItemTitle = styled.h2`
  color: #0f1624;
  font-size: 18px;
  line-height: 26px;
  text-align: start;
`
export const DropDownItemDesc = styled.p`
  color: #0f1624;
  opacity: 0.5;
  font-size: 14px;
  line-height: 22px;
  text-align: start;
`

Then import in as necessary

import { DropDownIcon, DropDownItem, DropDownItemDesc, DropDownItemTitle, DropDownTextContainer } from './NavDropDown'

Styled-Normalize Package

May want to use in conjunction with CSS-normalize library for styled-components. The original mormalize.cs is pulled from necolas/normalize.css and parsed into styled-ready format. 83k weekly downloads.

npm install --save styled-normalize

Check out their docs here.

// styles/index.js
import { createGlobalStyle } from 'styled-components'
import { normalize } from 'styled-normalize'
 
export const GlobalStyle = createGlobalStyle`
  ${normalize}
 
  // You can continue writing global styles here
  body {
    padding: 0;
    background-color: black;
  }
`
 
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
 
import { GlobalStyle } from './styles'
import { App } from './app'
 
const Root = () => (
  <React.Fragment>
    <GlobalStyle />
    <App />
  </React.Fragment>
)
 
ReactDOM.render(<Root />, document.querySelector('#root'))

Add a Suffix to Reduce Confusion?

One of the drawbacks I see to Styled-components is that is increases the number of components and starts to cloud what comes from Next, what is a page component, what is an icon. Maybe we can reduce this confusion by adding an SC to the end of the component name so you know it is a styled-components component. Could also do GSC for a global styled-component.

Tagged Template Literal

A tagged template literal is when you have a function pass an argument a template string. Tagged template literals call a function (the tag function) with an array of any text segments from the literal followed by arguments with the values of any substitutions.

// Untagged, these create strings:
`string text`
`string text line 1
 string text line 2`
`string text ${expression} string text`
// Tagged, this calls the function "example" with the template as the
// first argument and substitution values as subsequent arguments:
example`string text ${expression} string text`

Alternatives

Sass: I am familiar with; very popular. The learning curve for styled-components is steeper than that of Sass/SCSS. But with styled-components, you can still use plain CSS. You don’t have the flexibility to do things both ways with Sass. Finally, Sass is more likely to be found in legacy projects. Styledcomponents are rendered only if the component is on screen, while Sass is processed anyway in most cases,

CSS Modules: Built in to Next out of the box, not a huge fan of the syntax. CSS Modules is not a version of “CSS-in-JS.”

className={styles.whatEvs}

A pretty good article here.

“Technically, CSS Modules transforms style names using a hash-based on the filename, path, style name. Styled-components handles styles in JS runtime, adding them as they go to the head HTML section (<head>).”

Per the article here, choosing a styling system can be tricky. As a developer, you will obviously have a preference but you should know multiple approaches.

CSS-in-JS

Styled components is a form of CSS-in-JS. It is important to note that not all developers like to mix their CSS with JS.

Syntax Take

Episode 1/20/21 – Scott says he’s been getting away from styled-components because he loves CSS variables. Has base stuff and has been getting so good at writing CSS that is doing less and less overrides. Wes says he writes everything himself. Has lots of projects and some uses BEM, some uses styled-components, has been using styled-components but not the theming values from styled-comps, very rarely uses the ability to stick JS data into the style-components, but still likes styled-components for scoping CSS, making reusable components, writing CSS like it is regular CSS.

Episode 11/25/2020 – If you like CSS Modules, go for it. At the end of the day is doesn’t matter. Wes and Box prefer the CSS-in-JS approach. Scott doesn’t like the syntax for CSS modules.