Tous les articles

Front-End

Passing TypeScript React components native HTML attributes

Par Sarah Lancaster3 min de lecture

Blue and purple background with curly braces centered in the frame
AbstractTypeHEader

It’s important and useful to pass native HTML element attributes onto TypeScript + React components that manage and extend them.

Here’s how:
Take this simple input component which you can pass an isInvalid prop which adds a custom CSS class:

interface CustomInputProps {
  isInvalid?: boolean;
  onChangeCallback?: (value: string) → void;
}

export const Input = ({ isInvalid, onChangeCallback}: CustomInputProps) => {
  const handleChange = (event: ChangeEvent<HTMLInputElement>) = { 
    onChangeCallback & onChangeCallback(event.target.value);
  };

  const classes = `input $fisInvalid ? "input--invalid": ""}`;

  return ‹input className={classes} onChange={handleChange} ^;
};

Here it is being used in a React app:

export default function App() {
  const inputInvalid = false;
  return (
    <div className="App">
      <form>
        <label htmlFor="pin"›Enter name: </label>
        <nput name="name" id="name" isInvalid={inputInvalid}
      <form>
    <div>
  );
}
Screenshot of the UI of the plain black input. The full code is available at the sandbox link below.

What happens if we want this to be a password input? Hide the value? Have a minimum length? To be required?
Do we have to add all the relevant HTML attributes for our input to CustomInputProps?
Should we create a new component?

Nope! We can use the InputHTMLAttributes interface exported from React.

React exports a set of generic interfaces that correspond to various HTML elements.

Screenshot of the type definitions for typescript InputHTMLAttributes exported by React in the node modules

We can create a new TypeScript type which combines our custom interface and the InputHTMLAttributes interface.

type InputProps = InputHTMLAttributes<HTMLInputElement>& CustomInputProps;

We can use ES6 rest operator to spread any props passed into the React Input component into the element

export const Input = ({
  isInvalid,
  onChangeCallback,
  ...rest
}: InputProps) => {

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    onChangeCallback && onChangeCallback(event.target.value);
  };

  const classes = `input $fisInvalid ? "input--invalid": ""}`;

  return <input className={classes} onChange={handleChange} {... rest} />;
};


Now we can use all the attributes available on the input element (with no type errors).

export default function App() {
  const [inputInvalid, setInputInvalid] = useState(false);

  const handleInput = (value: string) => {
    if (value.length > 4) {
      setInputInvalid(true);
    } else {
      setInputInvalid(false);
    }
  };
  
  return (
    <div className="App">
      <form>
        <label htmlFor="pin">Enter pin: </label>
        <Input
          name="pin"
          id="pin"
          isInvalid={inputInvalid}
          type="password"
          minLength={4}
          required
          onChangeCallback={handleInput}
        />
      </form>
    </div>
  );
}
Screenshot of the UI of the typescript react input changed as a result of a prop change - black to red. The full code is available at the sandbox link below.

For values like className which we are passing onto the component internally, you need to handle the aggregate manually if you want to also pass a className as a prop.

export const Input = ({
  isInvalid,
  onchangeCallback,
  ... otherProps
}: InputProps)= {
  const { className, ... rest } = otherProps;
  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    onChangeCallback && onChangeCallback(event.target.value);
  };

  const classes = `input ${isInvalid ? "input--invalid": ""} ${ className ? className : ""
  }`;

  return <input className={classes} onChange={handleChange} {... rest} />;
};

Our final input JSX here has a mix of props typed both from the InputHTMLAttributes type and our custom type.

<Input
  name="pin"
  id="pin"
  isInvalid={inputInvalid}
  type="password"
  minLength={4}
  required
  onChangeCallback={handleInput}
  className="input--pin"
/>
Screenshot of the input which has been transformed into a password input thanks to the native HTML type=password prop. The full code is available at the sandbox link below.

Hopefully this guide helps you create more flexible, useful typesafe React components. You may use this Sandbox to play with actual code & typings demonstated in this article.

More tech articles I've written:

  • Peut-on aujourd’hui exercer uniquement en tant que spécialiste de l’intégration web ? Est-ce que ce métier existe toujours face à celui de dev front-end généraliste, censé tout savoir, tout maîtriser et tout livrer ? L’IA est-elle la nouvelle intégratrice dans l’équipe ? Ces questions méritent d’être posées car derrière elles, se cache une réalité : la valeur de l'intégration web est maintenant minimisée et le métier est considéré comme dépassé, voire absorbé par d’autres fonctions. Pourtant, i
  • JSX un standard de facto pour construire vos UI
    JSX est l'acronyme de "JavaScript XML", ce qui reflète la nature de cette syntaxe qui permet d'écrire des structures similaires à du XML, en particulier du HTML et du SVG, directement dans du code JavaScript. Il a été créé lors du développement de React: les équipes de développement de chez Facebook cherchaient une solution pour rendre le développement d'interfaces intuitif et déclaratif. Bien que n'étant pas un standard officiel, le JSX est un standard de facto dans le développement web modern
  • Conférence de Temal Kujar à React Paris
    Le 22 mars 2024, s'est tenue la conférence React Paris, une journée dédiée aux conférences et au networking, organisée par la communauté BeJS. Cet événement a offert aux participants une opportunité unique de découvrir de nouvelles perspectives, d'approfondir leurs connaissances et d'établir des contacts avec des pairs issus de divers horizons.  Les nombreuses conférences ont exploré divers aspects de React, allant de l'avenir de l'intelligence artificielle sur le web aux avancées des plateforme