2

I'm trying to ensure my menu items are mounted in the DOM for SEO purposes. I'm using a MUI Select component for a dropdown nav and am passing in keepMounted props which are ultimately spread on the Popper component via MenuProps.

I can see the menu items HTML (ul > li) mounted in my inspector but when I inspect source or CURL they don't exist.

const TopicSelect = ({
  classes,
  width,
  selectClassName,
  topicData,
  selectedValue,
  onChange,
}) => {
  const isMobile = isWidthDown('xs', width)

  return (
    <FormControl data-testid="TopicSelect-formControl" variant="filled">
      <InputLabel className={classes.label} htmlFor="topic-select-input">
        Filter by topic
      </InputLabel>
      <Select
        data-testid="TopicSelect-select"
        className={classnames(classes.select, selectClassName)}
        onChange={onChange}
        value={selectedValue}
        autoWidth
        native={isWidthDown('xs', width) ? true : false}
        input={
          <FilledInput
            name="topic"
            id="topic-select-input"
            className={classes.filledInput}
            disableUnderline
          />
        }
        MenuProps={{
          style: {
            zIndex: 1500,
          },
          keepMounted: true,
        }}
      >
        {isMobile
          ? [
              <option value="" />,
              topicData.map(topic => (
                <option
                  key={`${topic.text}-LearnNav-menuItem`}
                  value={topic.path}
                >
                  {topic.text}
                </option>
              )),
            ]
          : [
              <MenuItem key="none" value="">
                <em>None</em>
              </MenuItem>,
              topicData.map(topic => (
                <MenuItem
                  key={`${topic.text}-LearnNav-menuItem`}
                  value={topic.path}
                >
                  {topic.text}
                </MenuItem>
              )),
            ]}
      </Select>
    </FormControl>
  )
}

I would expect the menu items HTML to be rendered to the DOM because that's what the prop is intended to do. Not sure why they're not rendered during SSR.

Has anyone encountered this problem before? Is there a way to make sure these items are mounted that I'm overlooking?

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
Adam Bohannon
  • 108
  • 1
  • 2
  • 8

1 Answers1

7

The discussions I have seen regarding the keepMounted prop on Menu have been related to accessibility rather than SEO/SSR (though I see now that the prop description on Modal mentions SEO). For the accessibility use cases, a slight delay on the rendering of the menu items (rather than being part of the initial rendering) will not cause any issues.

The delay is due to Portal (Menu uses Popover which uses Modal which uses Portal) triggering the rendering of its children via an effect.

The only way I see to work around this is to leverage the disablePortal prop (in addition to keepMounted) on Menu/Popover/Modal/Portal.

        <Select
          value={state.age}
          onChange={handleChange}
          inputProps={{
            name: "age",
            id: "age-simple"
          }}
          MenuProps={{ keepMounted: true, disablePortal: true }}
        >
          <MenuItem value="">
            <em>None</em>
          </MenuItem>
          <MenuItem value={10}>Ten</MenuItem>
          <MenuItem value={20}>Twenty</MenuItem>
          <MenuItem value={30}>Thirty</MenuItem>
        </Select>

Edit Select menu items on initial render

I don't use SSR myself and haven't tested this solution in an SSR environment, but I believe it should do the trick.

Code resources:

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198