skip to Main Content

I’m building a blog site with MDX & NextJS. I’m defining a custom image component that will make use of the Next <Image> component. The issue I’m having is relatively small, but I’ve been struggling to find an answer to it.

The basic question is: If I have a variable of string | undefined, is there any way in React to only pass that variable to an optional prop if it’s defined? I’m pretty new to React, so this might be something super basic.

My reasoning for needing this is that the custom MDX image component should take ImgHTMLAttributes<HTMLImageElement> as props. All of these props are defined as someType | undefined, which makes sense since HTML attributes are optional.

My issue comes in passing these props down to Next image. How do I pass the optional src, alt, width, and height props when they are defined in the original image but not when they aren’t?

The specific error I’m getting is Type 'string | undefined' is not assignable to type 'string | StaticImport', which makes perfect sense.

I tried something like this:

    const src = props.src != undefined ? props.src : "";
    const alt = props.alt != undefined ? props.alt : "";
    const width = props.width != undefined ? props.width : "";
    const height = props.height != undefined ? props.height : "";

which was ugly, and also didn’t really solve my problem. I don’t want to be passing width when its undefined for example.

3

Answers


  1. This might be a little messy, but you could have a function that handles checking for the different props you want to watch. Inside this function you could return true if the props are present, and return false if some aren’t. Then, you could do conditional rendering depending on the truthy value of said function.

    const hasProps = propsObject => {
      // if props present, return true
      // otherwise return false
    }
    
    // then return something like this
    return (
     {hasProps ?
      <Foo
       src={src}
       alt={alt}
       width={width}
       height={height} /> 
       : 
      <Bar />
     }
    )
    

    OR Maybe you want to be able to check for each prop individually instead of all or none of them. In that case, I’d probably handle that conditionally in the components that’s receiving the props. This is probably how I would handle this situation.

    <Foo
     src={src ? src : undefined}
     alt={alt ? alt : undefined}
     width={width ? width : undefined}
     height={height ? height : undefined} />
    
    // Handle receiving props in Foo
    function Foo(props) {
     const [width, setWidth] = useState(props.width)
     ...
    }
    
    Login or Signup to reply.
  2. You can try this solutions. It looks a little bulky and ugly, but it can solve your problem.

    const src = undefined;
    const alt = 1;
    const width = 100;
    const height = undefined;
    
    const props = {
        ...(src && { src }),
        ...(alt && { alt }),
        ...(width && { width }),
        ...(height && { height }),
    }; # <-- Here will be only "alt" and "width"
    
    return (<Image {...props}/>);
    
    Login or Signup to reply.
  3. For the properties you need to supply defaults for instead of undefined, you use nullish coalescing, like this:

    export function ImageWrapper(props: ImageWrapperProps) {
        return <Image
            {...props}
            src={props.src ?? ValidDefaultSrc}
            alt={props.alt ?? ValidDefaultAlt}
        />;
    }
    

    The order is important there, first you spread out all of the properties from props, then override src and alt with versions with defaults. Those are the only two required properties in the props for the Next Image component.

    Playground link

    Note that I haven’t just used "" as the defaults. The src and alt props are required on Image for a reason. I don’t use Next.js, but it seems unlikely that src="" (in particular) is a valid value, and alt="" may be valid, but it’s not accessible to users of screen readers and such. But I suspect you just used those in the question as placeholders.

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