Extending regular React components with styled-components

The docs for styled-components show its default styled export accepts a single argument:

styled
This is the default export. This is a low-level factory we use to create the styled.tagname helper methods.
Arguments
component / tagname
Description
Either a valid react component or a tagname like 'div'.

I’ve emphasized “valid react component” here because they explicitly aren’t saying this has to be a React component created by styled, although traditionally that is how this is used (as well as documented under their Extending Styled section). An example of this is shown below:

const RedBox = styled.div`
  border: 1px solid black;
  color: red;
`;

// Traditionally, the argument you pass to `styled` is
// a react element *created by a previous `styled` call*
const BlueBox = styled(RedBox)`
  color: blue;
`;

function Example() {
  return (
    <div>
      <RedBox>I am a red box</RedBox>
      <BlueBox>I am a blue box</BlueBox>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/styled-components.js"></script>

<div id="root"></div>

Nothing unsurprising with the above.

However, my question is what if you pass a non styled component as the argument to the styled call? Shouldn’t that returned element also get the styles applied?

Consider the following simple example:

// Create two simple components, one functional, one class-based
const BoxFunctional = (props) => <div>{props.children}</div>;
class BoxClass extends React.Component {
  render() {
    return <div>{this.props.children}</div>
  }
}

// Here, I pass a functional React Component to `styled`
const RedBoxFunctional = styled(BoxFunctional)`
  color: red;
  border: 1px solid black;
`;
// Again, passing another regular React component, this time a class component
const RedBoxClass = styled(BoxClass)`
  color: red;
  border: 1px solid black;
`;

function Example() {
  return (
    <div>
      <p>The below two Boxes are regular react elements:</p>
      <BoxFunctional>I am a functional box</BoxFunctional>
      <BoxClass>I am a class box</BoxClass>
      <hr />
      <p>The below two boxes <em>should</em> be styled:</p>
      <RedBoxFunctional>I am a functional red box</RedBoxFunctional>
      <RedBoxClass>I am a class red box</RedBoxClass>
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-is.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/styled-components.js"></script>

<div id="root"></div>

Running the above snippet you can see that the styled components are not styled, despite them extending a “valid” react component.

Is there something I’m missing? Are the docs just incorrect? Can styled only apply styles to an existing react component that is made by a previous styled call?

Answer

I don’t really know where the confusion lies, you can pass any React component to the styled Higher Order Component.

The issue you have is that you aren’t trying to style the BoxFunctional or BoxClass components, but rather you are trying to style the JSX they render. You just need to proxy the className prop through to what each renders.

Styling any Component

The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.

const BoxFunctional = (props) => (
  <div className={props.className}>{props.children}</div>
);

class BoxClass extends Component {
  render() {
    return <div className={this.props.className}>{this.props.children}</div>;
  }
}

Demo

Edit extending-regular-react-components-with-styled-components

enter image description here

Leave a Reply

Your email address will not be published. Required fields are marked *