Search code examples
javascriptreactjsstyled-components

How to pass styles via props with styled components without having to create the component inside the component


I cannot find another way to give my props dynamic styles without creating the styled-component inside the Text component. For performance I'd like to create the component outside of the Text component.

Styled Component

import React, { ReactChildren } from 'react'
import styled from 'styled-components'


export const Text = ({ as = 'p', children, styles = {} }) => {
  const newStyles = Object.entries(styles).reduce((acc, [key, value]) => {
    return `${acc}${key}: ${value};`
  }, '')
  const Component = styled.p`
    ${newStyles}
    color: #000;
  `
  return <Component as={as}>{children}</Component>
}

Desired Usage

<Text styles={{ height: "10px" }} />

Output HTML

<p styles="height: 10px" />

enter image description here


Solution

  • The above code is confusing in that you want to apply styles to the DOM style property, but you're also applying them as CSS styles to a classname. There's no need to create this composed component because styled-components already handles as, children, and style properties without composing:

    Edit Styled Components - "Styles" Typescript

    Example 1:

    import React from "react";
    import styled, { Interpolation } from "styled-components";
    
    export type TextProps = {
      as?: string | React.ComponentType<any>;
      children: React.ReactNode;
      styles?: Interpolation<React.CSSProperties>;
    };
    
    const Component = styled.p<TextProps>`
      ${({ styles }) => styles}
      color: #000;
    `;
    
    export const Text = ({
      as,
      children,
      styles
    }: TextProps): React.ReactElement => (
      <Component styles={styles} as={as}>
        {children}
      </Component>
    );
    
    export default Text;
    

    Example 2:

    import styled from "styled-components";
    
    const Text = styled.p`
      color: #000;
    `;
    
    export default Text;
    

    Example 3:

    import * as React from "react";
    import styled from "styled-components";
    
    export type TextComponentProps = {
      className?: string;
      children: React.ReactNode;
      styles?: React.CSSProperties;
    };
    
    const TextComponent = ({
      children,
      className,
      styles
    }: TextComponentProps): React.ReactElement => (
      <p className={className} style={styles}>
        {children}
      </p>
    );
    
    const Text = styled(TextComponent)`
      color: #000;
    `;
    
    export default Text;
    

    Usage:

    import * as React from "react";
    import Box from "./Box";
    import Text from "./Text";
    import Text2 from "./Text2";
    import Text3 from "./Text3";
    import "./styles.css";
    
    const App = (): React.ReactElement => (
      <div className="app">
        <h1>Example 1 - "styles" as CSS Styles</h1>
        <Box>
          <Text styles={{ height: "10px" }}>Hello</Text>
          <Text as="h1">Goodbye</Text>
        </Box>
        <hr />
        <h1>Example 2 - "style" as DOM styles</h1>
        <Box>
          <Text2 style={{ height: "10px" }}>Hello</Text2>
          <Text2 as="h1">Goodbye</Text2>
        </Box>
        <hr />
        <h1>Example 3 - "styles" as DOM styles</h1>
        <Box>
          <Text3 styles={{ height: "10px" }}>Hello</Text3>
          <Text3 as="h1">Goodbye</Text3>
        </Box>
      </div>
    );
    
    export default App;
    

    Output: enter image description here

    On that note, it sounds like you might be trying to do something I did with composabled-styled-components, although I wouldn't recommend it because it doesn't work with SSR apps.