Search code examples
javascriptreactjsstyled-components

styled-component .attrs - React does not recognize prop


I am trying to pass down a prop into my styled component. It works as expected, but React throws the known 'Unknown Prop' error.

I tried to use the spread operator at numerous places but neither did work.

The styled component I want to pass down the prop to:

const StyledBackgroundImage = styled(BackgroundImage).attrs(({minHeight}) => ({
  minHeight: minHeight || "60vh",
}))`
  min-height: ${({minHeight}) => minHeight};
  /* ...  */
`;

The parent component:

const ImageWithText = ({imageData, minHeight, children}) => {
  return (
    <StyledBackgroundImage 
    Tag="div"
    backgroundColor={'#000000'}
    fluid={imageData}
    minHeight={minHeight}
    >
        {children}
    </StyledBackgroundImage>
  )
}

And how I use it on the page:

<ImageWithText imageData={data.headerBackgroundImage.childImageSharp.fluid} minHeight='50vh'>

I expect it to work, which does, but not without the following error:

Warning: React does not recognize the `minHeight` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `minheight` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
    in div (created by BackgroundImage)
    in BackgroundImage (created by Context.Consumer)
    in StyledComponent (created by ImageWithText__StyledBackgroundImage)
    in ImageWithText__StyledBackgroundImage (at ImageWithText.js:32)
    in ImageWithText (at pages/index.js:20)
    in section (created by Context.Consumer)
    in StyledComponent (created by LayoutComponents__Section)
    in LayoutComponents__Section (at pages/index.js:19)
    in main (at layout.js:10)
    in Layout (at pages/index.js:17)
    in IndexPage (created by HotExportedIndexPage)
    in AppContainer (created by HotExportedIndexPage)
    in HotExportedIndexPage (created by PageRenderer)
    in PageRenderer (at json-store.js:93)
    in JSONStore (at root.js:51)
    in RouteHandler (at root.js:73)
    in div (created by FocusHandlerImpl)
    in FocusHandlerImpl (created by Context.Consumer)
    in FocusHandler (created by RouterImpl)
    in RouterImpl (created by Context.Consumer)
    in Location (created by Context.Consumer)
    in Router (created by EnsureResources)
    in ScrollContext (at root.js:64)
    in RouteUpdates (at root.js:63)
    in EnsureResources (at root.js:61)
    in LocationHandler (at root.js:119)
    in LocationProvider (created by Context.Consumer)
    in Location (at root.js:118)
    in Root (at root.js:127)
    in _default (at app.js:65)

Solution

  • Update: Use transient props

    With the release 5.1.0 you can use transient props. This way you do not need an extra wrapper i.e. unnecessary code is reduced:

    Transient props are a new pattern to pass props that are explicitly consumed only by styled components and are not meant to be passed down to deeper component layers. Here's how you use them:

    const Comp = styled.div`
      color: ${props => props.$fg || 'black'};
    `;
    
    render(<Comp $fg="red">I'm red!</Comp>);
    

    Note the dollar sign ($) prefix on the prop; this marks it as transient and styled-components knows not to add it to the rendered DOM element or pass it further down the component hierarchy.

    The new answer should be:

    Component:

    <ImageWithText 
      $imageData={data.headerBackgroundImage.childImageSharp.fluid} // notice the '$'
      $minHeight='50vh'>
    

    Declaration of styled component:

    const StyledBackgroundImage = styled(BackgroundImage).attrs(({$minHeight}) => ({
      minHeight: minHeight || "60vh",
    }))`
      min-height: ${({$minHeight}) => $minHeight}; // notice the '$' before the prop name
      /* ...  */
    `;