DEV Community

Stephen Charles Weiss
Stephen Charles Weiss

Posted on • Originally published at stephencharlesweiss.com on

Reusing CSS With Styled Components

Styled components are really nice in compartmentalizing styling and keeping it close to the components that use it.

I wasn’t around for the “good old days” when we had a single master CSS file, but I can imagine how challenging it would be.

That’s why I thought it was interesting when I came across an example where I wanted to share styling across different components in a sudo-object-oriented way.

In this example, I have two components. They’re both inputs, but they’re built off of different bases (we’ll take this as granted for the sake of this example). The styling, however, is nearly identical with the second one building on the base of the first.

How could I re-use the styling with Styled-Components without duplicating code and potentially diverging in the future when I forget to update one?

What follows is a simple example demonstrating how I used css helper function within styled-components to effortlessly reuse my css without duplicating code.1

My original styled input:

import styled from styled-components;

export const DefaultInput = styled.input`
  border: 1px solid ${({error})=>( error ? `red` : `grey` )};
  border-radius: 4px;
  outline: none;
  padding: 0.5em;
`;

Since my second component actually isn’t going to be an input (it will be a button), I unfortunately can’t just do:

import styled from styled-components;

export const SecondComponent = styled(DefaultInput)`
  /* make changes as needed*/
`;

This would be ideal since I could simply extend the CSS I defined originally.

Enter the CSS helper function from styled components!

import styled, { css } from styled-components;

const baseInputStyles = css`
  border: 1px solid ${({error})=>( error ? `red` : `grey` )};
  border-radius: 4px;
  outline: none;
  padding: 0.5em;
`;

export const DefaultInput = styled.input`
  ${baseInputStyles}
`;

export const SecondComponent = styled.button`
  ${baseInputStyles}
    /* make changes as needed*/
`;

resources

Top comments (6)

Collapse
 
piavgh profile image
Hoang Trinh

Since my second component actually isn’t an input, I unfortunately can’t just do...

I don't understand the above statement.

Can you please explain what is the difference between your 2 examples?

Collapse
 
stephencweiss profile image
Stephen Charles Weiss

Oy - thank you for pointing that out! That's a mistake on my end.

I've updated the post to reflect what I meant. Bear in mind that the use of input and button are purely illustrative and could be anything. The bigger point is that the base elements are different and so can't simply be extended.

But we can use the css method to create a shared UI and eliminate some duplication.

Collapse
 
mglavall profile image
Marçal

Styled components has a property for this kind of use cases: styled-components.com/docs/api#as-...

If you want to keep all the styling you've applied to a component but just switch out what's being ultimately rendered (be it a different HTML tag or a different custom component), you can use the "as" prop to do this at runtime.

Collapse
 
stephencweiss profile image
Stephen Charles Weiss

Good point! When I wrote this I hadn't yet found polymorphic components. When I did a few weeks later, I wrote about them as well: stephencharlesweiss.com/blog/2019-... :)

(One point: I have had trouble with polymorphic components in my typescript app. Haven't dug into it too deeply yet, but just something to keep in mind)

Collapse
 
sakethtadimeti_24 profile image
saketh-tadimeti

Any idea how to reuse styles between vendor prefixes ? I'm building a custom audio component and want to target the slider thumb. since different browsers expose a different pseudo element, i'm having to add the same styles for each prefix.

Collapse
 
sakethtadimeti_24 profile image
saketh-tadimeti

const BaseThumbStyles = {
borderRadius: '50%',
border: 'none',
backgroundColor: 'var(--thumb-color)',
cursor: 'pointer',
position: 'relative',
boxSizing: 'border-box',
};

export const ProgressBar = styled.input
::-webkit-slider-runnable-track,
::-moz-range-track {
${() => css(BaseTrackStyles)};
}
;
for anyone looking, this works