Why and how to type HTML and CSS in React with TypeScript?

If you're only here for a quick reference to the syntax, here you go:

 
import React from 'react';
import * as CSS from 'csstype';

// Get type of `onClick` prop for the `HTMLButtonElement`
type OnClickType = React.HTMLAttributes<HTMLButtonElement>['onClick'] // React.MouseEventHandler<HTMLButtonElement> | undefined

// Type for a `HTMLButtonElement` with `ref` prop
type ButtonTypeWithRefType = React.DetailedHTMLProps<React.HTMLAttributes<HTMLButtonElement>, HTMLButtonElement>

// Get type for the `white-space` CSS property: https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
type WhiteSpaceType = CSS.Property.WhiteSpace // CSS.Globals | "-moz-pre-wrap" | "break-spaces" | "normal" | "nowrap" | "pre" | "pre-line" | "pre-wrap"

If you are curious about the why and how, please read on …

Why might we need type definitions for HTML and CSS?

I assume we agree that typed JavaScript in general is a good thing. But why might we need types for HTML and CSS? There are many reasons, but here are some of my common use cases:

  • We want to extend the API of our custom component to support and forward a prop from a html element, like the onClick prop from HTMLButtonElement.
  • We want to create a custom component that supports all the props of a HTML element, like HTMLButtonElement, including our custom props.
  • We want to extend the API of our custom component to support a CSS property.
  • Your using a css-in-js solution, and would like to add type checking to your css objects.

How do we use type definitions for HTML and CSS?

HTML


If we only want to pick out the type definition for one single prop, we can do as following:

 
// Get type of `onClick` prop for the `HTMLButtonElement`
type OnClickType = React.HTMLAttributes<HTMLButtonElement>['onClick'] // React.MouseEventHandler<HTMLButtonElement> | undefined

If we wan't the types for all the props (but without ref!):

 
// Type for a `HTMLButtonElement` without `ref` prop
type ButtonType = React.HTMLAttributes<HTMLButtonElement>

If we do want the ref prop as well, we have to specify the type like this:

 
// Type for a `HTMLButtonElement` with `ref` prop
type ButtonTypeWithRefType = React.DetailedHTMLProps<React.HTMLAttributes<HTMLButtonElement>, HTMLButtonElement>

If you don't like the verbose syntax, there is a package that aliases these types, called react-html-props:

 
import { ButtonProps } from "react-html-props";
// Tip: Import ButtonPropsWithoutRef if you don't care about the `ref` prop

const Button = (props: ButtonProps) => {
  return <button {...props}>{props.children}</button>;
};

CSS

For css, there is the package called csstype. To get the type of a single property, we can do as following:

 
import * as CSS from 'csstype';

// Get type for the `white-space` CSS property: https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
type WhiteSpaceType = CSS.Property.WhiteSpace // CSS.Globals | "-moz-pre-wrap" | "break-spaces" | "normal" | "nowrap" | "pre" | "pre-line" | "pre-wrap"

For type checking an css-in-js object, we can use it like this:

 
import * as CSS from 'csstype';

const style: CSS.Properties = {
  color: 'orangered',
  textTransform: 'uppercase',
};

I've created a playground where you can experiement with these types.