skip to Main Content

I am trying to create a React component in TypeScript, and I want to have a dynamic component that will wrap each of the child nodes that are passed to my component. The component is called Slider and here’s how I want it to work:

  1. Children are passed to the component
<Slider wrapper='div'>
    <p>Slide 1</p>
    <p>Slide 2</p>
    <p>Slide 3</p>
</Slider>
  1. Component logic wraps each child node in whatever component/string passed to the Slider component. If you pass:
<Slider wrapper='div'>...</Slider>

You’ll get:

<div>
    <p>Slide 1</p>
</div>

And if you pass:

<Slider wrapper={MyComponent}>...</Slider>

You’ll get:

<MyComponent>
    <p>Slide 1</p>
</MyComponent>

I wanted to be able to accept all the props within the component and then render everything with logic inside the component, but I keep getting a Typescript error:

Type { children: ReactNode; } has no properties in common with type IntrinsicAttributes. ts(2559)

I’ve looked at some other Stack Overflow questions and some Github issues, but I haven’t found solutions that help with my problem. I could certainly be overthinking or glossing over something obvious, so whatever help you can offer, I’m willing to hear!

Here’s my component code so far:

import { PropsWithChildren, ReactElement, ReactNode } from "react";

type SliderProps = PropsWithChildren<{
    wrapper?: string | (() => ReactElement);
}>;

export default function Slider({
    children,
    wrapper: Wrapper = "div",
}: SliderProps) {
    return <Wrapper>{children}</Wrapper>; // error on this line under <Wrapper>
}

3

Answers


  1. I don’t think you can just swap out a string for a component. I believe this is more appropriate.

    import { PropsWithChildren, ReactElement, ReactNode } from "react";
    
    type SliderProps = PropsWithChildren<{
        wrapper?: string | (() => ReactElement);
    }>;
    
    type SliderProps = PropsWithChildren<{
        wrapper?: (() => ReactElement);
    }>;
    
    
    export default function Slider({
        children,
        wrapper: Wrapper ,
    }: SliderProps) {
        if(Wrapper) return <Wrapper>{children}</Wrapper>
        return <div>{children}</div>
    }
    
    Login or Signup to reply.
  2. TypeScript expects the wrapper prop to be a string that refers to an HTML element or a React component type, but it’s not able to infer the correct type for Wrapper when you try to use it as a component.

    To fix this, you can define the wrapper prop to be of type React.ElementType, which is a built-in type that React provides for representing a component that can be rendered.

    Here’s how you can adjust your SliderProps type and Slider component:

    import { PropsWithChildren, ElementType } from "react";
    
    type SliderProps = PropsWithChildren<{
        wrapper?: ElementType;
    }>;
    
    export default function Slider({
        children,
        wrapper: Wrapper = "div",
    }: SliderProps) {
        return <Wrapper>{children}</Wrapper>;
    }
    
    Login or Signup to reply.
  3. You just need to use a more specific type than string so the compiler knows it’s limited to valid HTML tag names:

    import React, { PropsWithChildren } from 'react';
    
    type SliderProps = PropsWithChildren<{
        wrapper?: keyof JSX.IntrinsicElements | (() => JSX.Element);
    }>;
    
    export default function Slider({
        children,
        wrapper: Wrapper = "div",
    }: SliderProps) {
        return <Wrapper>{children}</Wrapper>;
    }
    

    Note that the Typescript JSX namespace is ambient, you don’t have to import it as long as you’ve imported React itself.

    Playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search