Skip to content

Commit aeb9f51

Browse files
Add CSSProperties and Ref reference pages (#816)
1 parent 850e2a4 commit aeb9f51

5 files changed

Lines changed: 368 additions & 19 deletions

File tree

docs/react-types/CSSProperties.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
title: CSSProperties
3+
---
4+
5+
`CSSProperties` is the type for inline styles passed via the `style` prop. It extends [`csstype`](https://github.com/frenic/csstype)'s `Properties<string | number>`, so every standard CSS property is covered with autocompletion and value validation.
6+
7+
## Parameters
8+
9+
`CSSProperties` does not take any parameters.
10+
11+
## Usage
12+
13+
### Inline styles
14+
15+
```tsx
16+
function Banner() {
17+
return (
18+
<div style={{ backgroundColor: "papayawhip", padding: 16 }}>Hello</div>
19+
);
20+
}
21+
```
22+
23+
### Reusable style objects
24+
25+
Pull style objects out to share between elements. Annotate with `CSSProperties` so TypeScript checks the values:
26+
27+
```tsx
28+
import { CSSProperties } from "react";
29+
30+
const card: CSSProperties = {
31+
borderRadius: 8,
32+
padding: 16,
33+
boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)",
34+
};
35+
36+
function Card({ children }: { children: ReactNode }) {
37+
return <div style={card}>{children}</div>;
38+
}
39+
```
40+
41+
### Typing a `style` prop on your own component
42+
43+
If your component forwards a style object, type the prop as `CSSProperties` directly — that's exactly what HTML elements use:
44+
45+
```tsx
46+
type BoxProps = {
47+
style?: CSSProperties;
48+
children?: ReactNode;
49+
};
50+
51+
function Box({ style, children }: BoxProps) {
52+
return <div style={{ padding: 16, ...style }}>{children}</div>;
53+
}
54+
```
55+
56+
## Values: numbers vs strings
57+
58+
For length-like properties, **numbers are interpreted as pixels** — React appends `px` automatically. Strings are passed through verbatim.
59+
60+
```tsx
61+
<div style={{ width: 100 }} /> // → width: 100px
62+
<div style={{ width: "100%" }} /> // → width: 100%
63+
<div style={{ width: "10rem" }} /> // → width: 10rem
64+
```
65+
66+
A handful of properties (such as `lineHeight`, `opacity`, `zIndex`, `flexGrow`) are unitless — passing a number leaves it unitless.
67+
68+
## Vendor prefixes
69+
70+
Vendor-prefixed properties are written in PascalCase, not with the leading hyphen:
71+
72+
```tsx
73+
const style: CSSProperties = {
74+
WebkitTransform: "rotate(45deg)",
75+
MozAppearance: "none",
76+
};
77+
```
78+
79+
## Custom properties (CSS variables)
80+
81+
`CSSProperties` deliberately has no index signature, so writing a CSS custom property triggers a TypeScript error:
82+
83+
```tsx
84+
// ❌ Type '{ "--accent": string; }' is not assignable to type 'CSSProperties'.
85+
<div style={{ "--accent": "tomato" }} />
86+
```
87+
88+
There are three common workarounds.
89+
90+
### 1. Type assertion (quickest)
91+
92+
```tsx
93+
<div style={{ "--accent": "tomato" } as CSSProperties} />
94+
```
95+
96+
Fine for one-off use, but you lose type-checking on the rest of the object.
97+
98+
### 2. Intersection with an indexed type (recommended)
99+
100+
Keep type-checking for normal properties while allowing any `--*` key:
101+
102+
```tsx
103+
type CSSPropertiesWithVars = CSSProperties & {
104+
[key: `--${string}`]: string | number;
105+
};
106+
107+
const style: CSSPropertiesWithVars = {
108+
color: "white",
109+
"--accent": "tomato",
110+
};
111+
112+
<div style={style} />;
113+
```
114+
115+
### 3. Module augmentation (when you have a fixed set of variables)
116+
117+
If your design system has a known list of CSS variables, augment `CSSProperties` once and get autocomplete everywhere:
118+
119+
```tsx
120+
// global.d.ts
121+
import "react";
122+
123+
declare module "react" {
124+
interface CSSProperties {
125+
"--accent"?: string;
126+
"--spacing"?: string | number;
127+
}
128+
}
129+
```
130+
131+
After this, `style={{ "--accent": "tomato" }}` type-checks with no assertion.
132+
133+
## Typing individual CSS values with `csstype`
134+
135+
If you want a prop that accepts a single CSS value — e.g. a color or a display value — import the underlying [`csstype`](https://github.com/frenic/csstype) package and use the `Property` namespace:
136+
137+
```tsx
138+
import type { Property } from "csstype";
139+
140+
type BadgeProps = {
141+
color?: Property.Color; // any valid CSS <color>
142+
display?: Property.Display;
143+
};
144+
```
145+
146+
`csstype` is already a transitive dependency of `@types/react`, so no extra install is needed — just import the types.
147+
148+
## CSS-in-JS libraries
149+
150+
Most CSS-in-JS libraries (Emotion, styled-components, Stitches, vanilla-extract, …) augment `CSSProperties` themselves to support library-specific features such as nested selectors, pseudo-classes as object keys, or theme tokens. If you see properties like `"&:hover"` accepted as keys, that's a library augmentation, not a feature of `@types/react`.

docs/react-types/ComponentProps.md

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22
title: ComponentProps<ElementType>
33
---
44

5-
`ComponentProps<ElementType>` constructs a type with all valid props of an element or inferred props of a component. It's an alias for `ComponentPropsWithRef<ElementType>`.
5+
`ComponentProps<ElementType>` constructs a type with all valid props of an element or inferred props of a component.
6+
7+
`@types/react` ships three related utilities:
8+
9+
| Type | What it gives you |
10+
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11+
| `ComponentProps<T>` | The props as declared by the component or element. |
12+
| `ComponentPropsWithRef<T>` | Same as `ComponentProps<T>`, plus `ref` for class components. For function components in React 19 the result is identical to `ComponentProps<T>` (since `ref` is already a regular prop). |
13+
| `ComponentPropsWithoutRef<T>` | `ComponentProps<T>` with any `ref` prop stripped out. Useful when you spread props onto a child element and don't want `ref` to leak. |
614

715
:::note
8-
**React 19+**: `ComponentPropsWithRef<ElementType>` is recommended as refs are now passed as props in function components. (See [forwardRef/createRef](/docs/basic/getting-started/forward_and_create_ref))
16+
**React 19+:** `ComponentProps<T>` is usually all you need — `ref` is just a regular prop for function components. Reach for `ComponentPropsWithoutRef<T>` when you specifically need to remove `ref` from a spread.
917

10-
**React ≤18**: Prefer `ComponentPropsWithRef<ElementType>` if ref is forwarded and `ComponentPropsWithoutRef<ElementType>` when ref is not forwarded.
18+
**React ≤18:** Prefer `ComponentPropsWithRef<T>` when refs are forwarded, and `ComponentPropsWithoutRef<T>` when they are not.
1119
:::
1220

1321
## Parameters
@@ -28,7 +36,7 @@ interface Props extends ComponentProps<"div"> {
2836
}
2937

3038
function Component({ className, children, text, ...props }: Props) {
31-
// `props` includes `text` in addition to all valid `div` props
39+
// `props` includes all valid `div` props (minus the ones destructured above)
3240
}
3341
```
3442

@@ -49,28 +57,19 @@ type MyType = ComponentProps<typeof Component>;
4957
// ^? type MyType = Props
5058
```
5159

52-
#### Infer specific prop type
60+
#### Infer a specific prop type
5361

54-
The type of a specific prop can also be inferred this way. Let's say you are using an `<Icon>` component from a component library. The component takes a `name` prop that determines what icon is shown. You need to use the type of `name` in your app, but it's not made available by the library. You could create a custom type:
62+
The type of a specific prop can also be inferred. Let's say you are using an `<Icon>` component from a component library. The component takes a `name` prop that determines what icon is shown. You need to use the type of `name` in your app, but it's not made available by the library. You could create a custom type:
5563

5664
```tsx
5765
type IconName = "warning" | "checkmark";
5866
```
5967

60-
However, this type is not really reflecting the actual set of icons made available by the library. A better solution is to infer the type:
68+
However, this type doesn't reflect the actual set of icons the library exposes. A better solution is to infer the type by indexing into the inferred props:
6169

6270
```tsx
6371
import { Icon } from "component-library";
6472

6573
type IconName = ComponentProps<typeof Icon>["name"];
6674
// ^? type IconName = "warning" | "checkmark"
6775
```
68-
69-
You can also use the `Pick<Type, Keys>` utility type to accomplish the same thing:
70-
71-
```tsx
72-
import { Icon } from "component-library";
73-
74-
type IconName = Pick<ComponentProps<typeof Icon>, "name">;
75-
// ^? type IconName = "warning" | "checkmark"
76-
```

docs/react-types/ReactNode.md

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22
title: ReactNode
33
---
44

5-
`ReactNode` is a type that describes what React can render.
5+
`ReactNode` is a type that describes what React can render. It's a union of every value React accepts as a child:
6+
7+
- `ReactElement` (the result of JSX, `createElement`, or `cloneElement`)
8+
- `string`
9+
- `number`
10+
- `bigint`
11+
- `boolean` (`true` and `false` render as nothing)
12+
- `null`
13+
- `undefined`
14+
- `Iterable<ReactNode>` (so arrays of nodes, but also any iterable)
15+
- `ReactPortal`
16+
- `Promise<ReactNode>` (for async Server Components — React unwraps the promise via Suspense)
617

718
## Parameters
819

@@ -12,7 +23,7 @@ title: ReactNode
1223

1324
### Typing `children`
1425

15-
The most common use case for `ReactNode` is typing `children`.
26+
The most common use case for `ReactNode` is typing `children`.
1627

1728
```tsx
1829
import { ReactNode } from "react";
@@ -26,7 +37,7 @@ function Component({ children }: Props) {
2637
}
2738
```
2839

29-
`<Component>` accepts anything that React can render as `children`. Here are some examples:
40+
`<Component>` accepts anything that React can render as `children`:
3041

3142
```tsx
3243
function Examples() {
@@ -37,6 +48,7 @@ function Examples() {
3748
</Component>
3849
<Component>Hello</Component>
3950
<Component>{123}</Component>
51+
<Component>{42n}</Component>
4052
<Component>
4153
<>Hello</>
4254
</Component>
@@ -48,3 +60,69 @@ function Examples() {
4860
);
4961
}
5062
```
63+
64+
### Async Server Components
65+
66+
A Server Component can be `async` and return a `Promise<ReactNode>`. React unwraps the promise through the nearest `<Suspense>` boundary:
67+
68+
```tsx
69+
// Server Component
70+
async function UserProfile({ userId }: { userId: string }) {
71+
const user = await fetchUser(userId);
72+
return <p>{user.name}</p>;
73+
}
74+
75+
function Page() {
76+
return (
77+
<Suspense fallback={<p>Loading…</p>}>
78+
<UserProfile userId="42" />
79+
</Suspense>
80+
);
81+
}
82+
```
83+
84+
A bare `Promise<ReactNode>` can also be passed as children directly — useful for streaming patterns where the parent kicks off the work and the child resolves it:
85+
86+
```tsx
87+
function Page() {
88+
const userPromise = fetchUser(); // Promise<ReactNode>
89+
return <Suspense fallback={<Loading />}>{userPromise}</Suspense>;
90+
}
91+
```
92+
93+
## `ReactNode` vs `ReactElement` vs `JSX.Element`
94+
95+
These three types are often confused because all three appear when you write JSX. They are not interchangeable:
96+
97+
- **`ReactNode`** is the broadest: anything React can render, including primitives, `null`, arrays, and elements.
98+
- **`ReactElement`** describes only the object produced by JSX or `createElement` — it has `type`, `props`, and `key`. A `string` is _not_ a `ReactElement`.
99+
- **`React.JSX.Element`** is essentially `ReactElement<any, any>` — what the JSX transform infers for a JSX expression.
100+
101+
### Use `ReactNode` for `children`
102+
103+
`ReactNode` is the correct type for any prop that receives children-like content, because a caller might pass a string, an array, or `null`:
104+
105+
```tsx
106+
type Props = { content: ReactNode };
107+
108+
<MyComponent content="hello" /> // ✅ string is a ReactNode
109+
<MyComponent content={<span>hi</span>} /> // ✅ element is a ReactNode
110+
<MyComponent content={null} /> // ✅ null is a ReactNode
111+
```
112+
113+
### Don't use `ReactNode` as a function-component return type
114+
115+
A function component's return type should be what React allows components to _return_, not what it allows them to _receive_. Returning a plain `ReactNode` (which includes `bigint`, `Promise<ReactNode>`, etc.) is broader than what TypeScript wants to see from a JSX-rendered component. Let TypeScript infer the return type, or use `React.JSX.Element` / `ReactElement` if you must annotate:
116+
117+
```tsx
118+
// 👎 too broad, and historically caused issues when used in JSX
119+
const MyComponent = (): ReactNode => "hello";
120+
121+
// 👍 let TS infer
122+
const MyComponent = () => "hello";
123+
124+
// 👍 explicit
125+
const MyComponent = (): React.JSX.Element => <span>hello</span>;
126+
```
127+
128+
[Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new).

0 commit comments

Comments
 (0)