CSS variables with React and TypeScript

Here’s a common issue I bump into when using React and TypeScript. I try to initialize a CSS variable in my component’s style prop like so.

      function MyComponent() {
	return <div style={{ "--my-variable": 0 }}>...</div>
}

    

But I hit a type error:

      Object literal may only specify known properties, and '"--my-variable"' does not exist in type 'Properties<string | number, string & {}>'.

    

This happens because React uses the Properties interface from the “csstype” library to define the allowed properties of the style prop. This includes lots of known CSS properties, but it doesn’t know about my custom CSS variables.

If you follow the style prop’s type definitions with Cmd + Click, you’ll find this comment in the React code:

      export interface CSSProperties extends CSS.Properties<string | number> {
	/**
	 * The index signature was removed to enable closed typing for style
	 * using CSSType. You're able to use type assertion or module augmentation
	 * to add properties or an index signature of your own.
	 *
	 * For examples and more information, visit:
	 * https://github.com/frenic/csstype#what-should-i-do-when-i-get-type-errors
	 */
}

    

As it explains, you can work around the error a type assertion like this:

      function MyComponent() {
	return <div style={{ "--my-variable": 0 } as React.CSSProperties}>...</div>
}

    

Or you can use module augmentation. I prefer the augmentation approach as it maintains the safety of the original types, but don’t get errors when I decide to use CSS variables.

It doesn’t really matter what you call this augmentation file or where you put it, all that matters is it’s in a .d.ts file that TypeScript can see (anywhere alongside your source files is usually included). I usually follow the convention that Vite and Next.js use for vite-env.d.ts and next-env.d.ts by calling this react-env.d.ts to signal that I’m augmenting types from React.

You could specify a strict list of allowed CSS variable types:

      // react-env.d.ts

import "react"

declare module "react" {
	interface CSSProperties {
		"--my-variable"?: number
	}
}

    

Or you can use template literal type to allow arbitrary CSS variables:

      // react-env.d.ts

import "react"

declare module "react" {
	interface CSSProperties {
		[key: `--${string}`]: string | number | undefined
	}
}

    

Now you’ll continue to get errors if you typo a regular style property, but properties prefixed with -- will be allowed with the values of your choosing.

For me, the template literal approach is a good balance of safety and flexibility.