20

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)
psteinroe
  • 493
  • 1
  • 6
  • 18

3 Answers3

57

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
  /* ...  */
`;
EliteRaceElephant
  • 7,744
  • 4
  • 47
  • 62
6

To prevent components that pass all props to a DOM element, create a wrap of your component but do not pass your custom props to the children component via "Object destructuring". In this way, you can style the wrapped component and styled-components can access to the prop but the children will not have custom properties, and your warning will disappear.

import React from 'react';
import styled from 'styled-components';
import Typography from '@material-ui/core/Typography'

const WrappedTypography = ({ maxWidth, ...props }) => {
  return <Typography {...props} />
}

const Text = styled(WrappedTypography) `
  ${({ maxWidth }) => maxWidth ? `max-width: ${maxWidth}` : null}
`
export default Text;

You can learn more about deestructuring at Mozilla docs.

kartojal
  • 61
  • 1
  • 2
2

The correct syntax should be:

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

Edit: Actually, if you just want to have a default style, why don't you have it in the styles directly:

const StyledBackgroundImage = styled(BackgroundImage)`
  min-height: ${({ minHeight }) => minHeight || '60vh'};
  /* ...  */
`;
`
Clarity
  • 10,730
  • 2
  • 25
  • 35
  • Thank you for your answer. Unfortunately, both syntax still throw the same error. Any idea what goes wrong here? – psteinroe Aug 21 '19 at 11:53
  • Make sure I've updated to the latest styled-component version. The error happens because some of your components passes `minHeight` prop directly to DOM element. This often happens when you do smth like ``. – Clarity Aug 21 '19 at 11:56
  • I am already working with the current versions (styled-components: 4.3.2, gatsby-plugin-styled-components: 3.1.2) and I do not use the spread operator as you see in the code in my question. That is why I am wondering where this comes from. Any idea? – psteinroe Aug 21 '19 at 12:24