How to create component with dynamic tag?

I trying to create a component for Typography purposes.

In pure React it’s will look like a:

// more props
const Typography = ({ tag: Tag = 'div', children}) => {
  
  // some logic and props
  return <Tag>{children}</Tag>
}

export default function App() {
  return (
    <div className="App">
      <Typography tag="h1">Hello, React</Typography>
      <Typography tag="h2">Start editing to see some magic happen!</Typography>
    </div>
  );
}

A simple approach might use something like this, where you declare each of the valid container tags:

module Typography = {
  type tag = [#div | #h1 | #h2]
  @react.component
  let make = (~tag: tag, ~children: React.element) => {
    switch tag {
    | #div => <div> {children} </div>
    | #h1 => <h1> {children} </h1>
    | #h2 => <h2> {children} </h2>
    }
  }
}

let _ = <Typography tag=#h1> {"Hello, React"->React.string} </Typography>

A more dynamic approach that could allow invalid tags might look something like this:

module Typography = {
  @react.component
  let make = (~tag: string, ~children: React.element) => {
    React.createElementVariadic(
      ReactDOM.stringToComponent(tag),
      ReactDOM.domProps(), // No props for the tag
      [children],
    )
  }
}

let _ = <Typography tag="h1"> {"Hello, React"->React.string} </Typography>

Or perhaps a combination of the above two:

module Typography = {
  type tag = [#div | #h1 | #h2]
  @react.component
  let make = (~tag: tag, ~children: React.element) => {
    React.createElementVariadic(
      ReactDOM.stringToComponent((tag :> string)),
      ReactDOM.domProps(),
      [children],
    )
  }
}

let _ = <Typography tag=#h1> {"Hello, React"->React.string} </Typography>

I’m not an expert on those last two approaches, so someone else might be able to offer an improvement.

4 Likes