Skip to content

How do I provide props to styled elements in Typescript? #630

Closed

Description

@tal

I looked at the docs and other than creating a whole empty component purely for providing a definition of props I'm not sure of if it's possible to provide custom props to a styled element other than theme.

Example

image

For the above example I don't know how to make it not error.

Activity

  1. kitten commented on Mar 30, 2017

    @kitten
    Member

    @tal Assuming that TypeScript infers the generics correctly — Disclaimer 😉 I haven't used SC with TypeScript yet — you'll just need to type your interpolation argument:

    const Input = styled.input`
      border: ${(p: YourProps) => p.invalid ? 'red' : 'blue'};
    `
  2. tal commented on Mar 30, 2017

    @tal
    Author
  3. mxstbr commented on Apr 1, 2017

    @mxstbr
    Member

    /cc @styled-components/typers any ideas?

  4. jupl commented on Apr 2, 2017

    @jupl

    EDIT: Holdup. What I had just happened to fit my usecase.

    This currently seems to be the best approach, albeit a bit wordy.

  5. giladaya commented on Apr 5, 2017

    @giladaya

    @philpl 's approach worked for me

  6. beshanoe commented on Jun 7, 2017

    @beshanoe

    I'm using v2 and found this way more convinient for plain containers:

    function stuc<T>(El: string = 'div') {
      type PropType = T & { children?: JSX.Element[], className?: string }
      return (props: PropType) => <El className={props.className}>{props.children}</El>
    }
    
    const Container = stuc<{customProp?: boolean}>()
    
    const StyledContainer = styled(Container)`
      background: ${p => p.customProp ? 'red' : 'blue'};
    `
  7. patrick91 commented on Jun 7, 2017

    @patrick91
    Contributor

    @beshanoe hopefully we'll have a way to do this in TS (see: microsoft/TypeScript#11947)

  8. mxstbr commented on Jun 30, 2017

    @mxstbr
    Member

    Going to close this as there isn't much we can do right now about this, thanks for discussing!

  9. alloy commented on Jul 14, 2017

    @alloy

    This is what I use at the moment:

    import styled, { StyledFunction } from "styled-components"
    
    interface YourProps {
      invalid: boolean
    }
    
    const input: StyledFunction<YourProps & React.HTMLProps<HTMLInputElement>> = styled.input
    
    const Input = input`
      border: ${p => p.invalid ? 'red' : 'blue'};
    `
  10. atfzl commented on Jul 22, 2017

    @atfzl

    Extending @David-Mk's method to get theme types as shown in https://www.styled-components.com/docs/api#typescript

    // themed-components.ts
    import * as styledComponents from 'styled-components';
    import { ThemedStyledComponentsModule } from 'styled-components';
    
    import { ITheme } from 'theme'; // custom theme
    
    type StyledFunction<T> = styledComponents.ThemedStyledFunction<T, ITheme>;
    
    function withProps<T, U extends HTMLElement = HTMLElement>(
      styledFunction: StyledFunction<React.HTMLProps<U>>,
    ): StyledFunction<T & React.HTMLProps<U>> {
      return styledFunction;
    }
    
    const {
      default: styled,
      css,
      injectGlobal,
      keyframes,
      ThemeProvider,
    } = styledComponents as ThemedStyledComponentsModule<ITheme>;
    
    export { css, injectGlobal, keyframes, ThemeProvider, withProps };
    export default styled;
    // component
    
    import styled, { withProps } from 'themed-components';
    
    interface IProps {
      active?: boolean;
    }
    
    const Container = withProps<IProps, HTMLDivElement>(styled.div)`
      height: 100%;
      padding-right: 52px;
      display: flex;
      justify-content: center;
      align-items: center;
      color: ${props => props.theme.colors.black4};
      background-color: ${props => props.active ? props.theme.colors.white : props.theme.colors.grey};
    `;
  11. Igorbek commented on Jul 23, 2017

    @Igorbek
    Contributor

    Another option which allows to bypass explicit element type specification, but looking a bit awkward:

    import { ThemedStyledFunction } from 'styled-components';
    
    export const withProps = <U>() =>
        <P, T, O>(
            fn: ThemedStyledFunction<P, T, O>
        ): ThemedStyledFunction<P & U, T, O & U> => fn;

    so later:

    interface ButtonProps {
        active: boolean;
    }
    
    //                                           / note () here
    export const Button = withProps<ButtonProps>()(styled.div)`
      color: ${p => p.active ? 'red' : 'black'}
    `;
  12. jimthedev commented on Oct 18, 2017

    @jimthedev
    Contributor

    @Igorbek Have you noticed that vscode's typescript styled plugin seems to not work when using any of these syntaxes?

    Edit:

    In the mean time I've aliased styled to themed and then add props to each of the types I want to use. Could probably be optimized more to be less ugly:

    import * as themed from 'styled-components';
    
    //...
    
    interface IHeadingProps {
      active?: boolean;
    }
    
    const styled = {
      div: withProps<IHeadingProps>()(themed.div)
    };
    
    //...
    
    export const Heading = styled.div`
      font-size: 22px;
    `;

    Edit: Opened issue here: microsoft/typescript-styled-plugin#21

  13. 75 remaining items

  14. ni3t commented on Jun 6, 2019

    @ni3t

    The styled wrapper can be passed in a type like so:

    interface SomeInterface {
      awesome: boolean
    }
    
    const WrappedText = styled<SomeInterface>(Text)`
      color: ${({awesome}) => awesome ? "green" : "black"}
    `
  15. CMCDragonkai commented on Jun 18, 2019

    @CMCDragonkai

    @ni3t Is that a recent thing? When I try that with an interface type I get:

    ERROR in [at-loader] ./src/components/InverterAnalysis.tsx:153:32 
        TS2344: Type 'InverterAnalysisProps' does not satisfy the constraint '"symbol" | "object" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | ... 155 more ... | ComponentClass<...>'.
      Type 'InverterAnalysisProps' is not assignable to type 'ComponentClass<any, any>'.
        Type 'InverterAnalysisProps' provides no match for the signature 'new (props: any, context?: any): Component<any, any, any>'.
    
  16. yasso1am commented on Jun 18, 2019

    @yasso1am

    Can confirm that @ni3t 's recommendation worked just fine for me.

  17. hypeofpipe commented on Jun 28, 2019

    @hypeofpipe

    Thanks @ni3t 🎈

  18. joshuatshaffer commented on Jul 5, 2019

    @joshuatshaffer

    Thanks @ni3t. That helped a lot.

    @CMCDragonkai I think you tried something like styled<MyExtraProps>.button to get that error. You need to use styled.button<MyExtraProps>.

    To summarize:

    // Some properties that we use in our styles, 
    // but are not part of the underlying component's props.
    type MyExtraProps = {
        anExtraProp: WhateverType
    };
    
    // For basic HTML elements:
    const MyStyledComponent = styled.button<MyExtraProps>`
        ... ${props => props.anExtraProp } ...
    `;
    
    // For extending components that support the className prop:
    const MyStyledComponent = styled<MyExtraProps>(AStyleableComponent)`
        ... ${props => props.anExtraProp } ...
    `;

    (P.S. The versions in my package.json are styled-components@^4.2.0, @types/styled-components@^4.1.16, and typescript@^3.3.4000.)

  19. paulmelnikow commented on Jul 11, 2019

    @paulmelnikow

    Based on the above comment and my experience using styled-components with React Native, it seems that styled<SomeInterface>(Text) in @ni3t's answer should be styled.Text<SomeInterface>.

  20. ni3t commented on Jul 25, 2019

    @ni3t

    Ah I've never tried it using the built-in styled.Whatever.

    Thanks for the feedback!

  21. openkalki commented on Aug 28, 2019

    @openkalki

    I came across this issue today and found something that worked for my use-case.

    I wanted to:

    1. Define my own props that I could pass into my React Functional Component
    2. Use "React.FC" interface so that my functional component uses the correct interface
    3. Use styled-components to change a style based on a prop that I pass down

    Here is how I solved it:

    import React from "react";
    import styled from "styled-components";
    
    interface LayoutProps {
      padding?: string;
    }
    
    const LayoutContainer = styled.div<LayoutProps>`
      display: flex;
      padding: ${props =>
        props.padding !== "undefined" ? props.padding : "0 20px"};
      align-items: center;
      justify-content: center;
      flex: 1;
      flex-direction: column;
    `;
    
    const Layout: React.FC<LayoutProps> = props => {
      return <LayoutContainer {...props}>{props.children}</LayoutContainer>;
    };
    
    export default Layout;
    
    

    Hope this helps people

  22. noelrappin commented on Aug 30, 2019

    @noelrappin

    So, I had this:

    const Foo = styled.span.attrs(props => ({ className: "button" }))`
    `
    

    Which was fine, but when I tried to add a prop type:

    const Foo = styled.span<IFooProps>.attrs(props => ({ className: "button" }))`
    `
    

    Then I get a TypeScript error, because it doesn't recognize attrs anymore.

    Is there a workaround here I'm missing?

  23. joshuatshaffer commented on Aug 31, 2019

    @joshuatshaffer

    @noelrappin Try this:

    const Foo = styled.span.attrs<IFooProps>(props => ({ className: "button" }))`
    `
    

    What's happening is that the <IFooProps> is you passing a type argument to a generic function.
    See https://www.typescriptlang.org/docs/handbook/generics.html

  24. glenwinters commented on Oct 29, 2019

    @glenwinters

    For those that find this issue via google, here's the answer in the documentation:
    https://www.styled-components.com/docs/api#using-custom-props

    Example:

    interface TitleProps {
      readonly isActive: boolean;
    };
    
    const Title = styled.h1<TitleProps>`
      color: ${props => props.isActive ? props.theme.colors.main : props.theme.colors.secondary};
    `
    
  25. mayowDev commented on Nov 8, 2019

    @mayowDev

    i was solving button styled component and this one worked for me

    import styled, { StyledComponent } from "styled-components"

    const ButtonContainer: StyledComponent<"button", any, {}, never>= styled.button your code here

  26. Kelbie commented on Feb 13, 2020

    @Kelbie
  27. paulmelnikow commented on Feb 13, 2020

    @paulmelnikow
  28. locked as resolved and limited conversation to collaborators on Feb 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions