@imfusion/web-ui 0.11.1-dev.55.gecb71e07 → 0.11.1-dev.56.gc2cf627a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -102,6 +102,11 @@
102
102
  - Forms use TanStack Form: `imf-web-ui-ux` names it for every form, the `imf-web-ui-conventions` `validation` topic
103
103
  validates user input in the form's validators with the Zod schema, and `imf-web-ui-audit` reports a form whose state and
104
104
  validation are written by hand.
105
+ - The `imf-web-ui-conventions` `react` topic splits into `composition`, `state`, and `effects`, and each rule names the
106
+ React version it needs. New rules from react.dev: pure render, no component defined inside another, no `ref.current`
107
+ during render, `ref` as a prop, `useId` for accessibility ids, error boundaries around UI units, one `status` over
108
+ exclusive booleans, pure reducers, no object or function effect dependencies, and `useEffectEvent` for values an effect
109
+ should not react to. React's `<form action>`, `useActionState`, and `useOptimistic` are the fallbacks without TanStack.
105
110
  - `web-ui-dev-new-component`, `web-ui-dev-update-component`, and `web-ui-dev-design-component` draft on the shared
106
111
  Web UI Component Drafts canvas: a block per component on its Storybook group's page, one row per fidelity rung.
107
112
  - `web-ui-dev-new-component` and `web-ui-dev-update-component` wait for the user to approve the design on the canvas
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imfusion/web-ui",
3
- "version": "0.11.1-dev.55.gecb71e07",
3
+ "version": "0.11.1-dev.56.gc2cf627a",
4
4
  "description": "The official Web UI component library for ImFusion web apps",
5
5
  "author": "ImFusion GmbH",
6
6
  "homepage": "https://imfusion.com",
@@ -2,7 +2,8 @@
2
2
 
3
3
  One block per topic, one box per topic section. Check a box only after checking that section's rules against the repo. A full
4
4
  audit checks every box; a scoped audit names its topics first and checks only their blocks. Blocks mirror each topic's `##`
5
- sections one to one — change a topic's sections and this file follows in the same commit.
5
+ sections one to one, and a topic split into a folder gets one `###` block per subtopic — change a topic's sections and this
6
+ file follows in the same commit.
6
7
 
7
8
  ## Library setup — `library-setup`
8
9
 
@@ -35,10 +36,23 @@ sections one to one — change a topic's sections and this file follows in the s
35
36
 
36
37
  ## React — `react`
37
38
 
39
+ ### Composition — `react/composition`
40
+
38
41
  - [ ] Composition: pages (smart containers), partials, dumb components
39
42
  - [ ] Compose, don't configure
43
+ - [ ] Render rules
44
+
45
+ ### State — `react/state`
46
+
40
47
  - [ ] Put state where its truth lives
48
+ - [ ] State shape
49
+ - [ ] Fallbacks without TanStack
50
+
51
+ ### Effects — `react/effects`
52
+
41
53
  - [ ] Effects: last resort, and named
54
+ - [ ] Dependencies
55
+ - [ ] Effect Events for values the effect should not react to
42
56
 
43
57
  ## Components — `components`
44
58
 
@@ -0,0 +1,54 @@
1
+ # React composition
2
+
3
+ Which role a component plays, and how parts combine.
4
+
5
+ ## Composition: pages (smart containers), partials, dumb components
6
+
7
+ - **Pages** live in `routes/`. They load data, read URL state, coordinate actions, and compose the view. Route files hold the
8
+ search schema, loaders, and pending, error, and not-found components. They do not own component CSS. Route-local views and
9
+ forms live in the route's `-` folder, as [project-structure.md](../project-structure.md) describes, and that folder's
10
+ `.module.css` files are component styles.
11
+ - **Partials** are compositions that more than one route uses, extracted because they read as a unit. They have light logic
12
+ and no styling of their own.
13
+ - **Layout components** arrange content with `Stack`, `Row`, grids, and token gaps, and wrap long-form text in `Prose`.
14
+ - **Dumb components** receive data and callbacks, render UI, and own their styles. They do not fetch, route, or contain
15
+ business rules.
16
+
17
+ ```tsx
18
+ function UserCard({ name, onEdit }: { name: string; onEdit: () => void }) {
19
+ return (
20
+ <Card.Root>
21
+ <Typo.P>{name}</Typo.P>
22
+ <Button onClick={onEdit}>Edit</Button>
23
+ </Card.Root>
24
+ );
25
+ }
26
+ ```
27
+
28
+ ## Compose, don't configure
29
+
30
+ Compose small parts instead of making one component configurable through many boolean props. Lift shared state to the nearest
31
+ common parent.
32
+
33
+ ## Render rules
34
+
35
+ These come from react.dev and hold for every component. Each names the React version it needs.
36
+
37
+ - **Render is pure (any version).** Never mutate props, state, context, or a value created before render. Mutating an object
38
+ the same render just created is fine.
39
+ - **No component defined inside another (any version).** A nested definition gets a new identity each render, so React
40
+ remounts it and its state resets. Define it at module level and pass data as props.
41
+ - **No `ref.current` during render (any version).** Read and write refs in effects and handlers, except for a lazy init. A
42
+ value that shapes the output belongs in state.
43
+ - **`ref` is a prop (React 19+).** Accept `ref` like any other prop in your own components; don't wrap them in `forwardRef`.
44
+ - **`useId` for accessibility ids (React 18+).** Use it for `htmlFor`, `aria-describedby`, and similar. Never use it for list
45
+ keys, and never hardcode its output in tests or selectors.
46
+ - **Error boundaries around UI units (any version).** Wrap a page section or widget, not every component. A boundary does not
47
+ catch errors from event handlers or async code; TanStack Query and Router surface those.
48
+ - **With the React Compiler on, no manual memoization by default.** `useMemo`, `useCallback`, and `React.memo` are only for a
49
+ measured case.
50
+
51
+ See [Keeping Components Pure](https://react.dev/learn/keeping-components-pure),
52
+ [Preserving and Resetting State](https://react.dev/learn/preserving-and-resetting-state),
53
+ [Referencing Values with Refs](https://react.dev/learn/referencing-values-with-refs), and
54
+ [`useId`](https://react.dev/reference/react/useId).
@@ -0,0 +1,59 @@
1
+ # React effects
2
+
3
+ When an effect is the right tool, and how to write the ones that stay.
4
+
5
+ ## Effects: last resort, and named
6
+
7
+ Use `useEffect` only to synchronize with something outside React. Do not use it for derived values, event responses, or
8
+ server sync.
9
+
10
+ A genuine effect belongs in a named custom hook. If it stays inline, name the callback:
11
+
12
+ ```tsx
13
+ useEffect(
14
+ function syncDocumentTitle() {
15
+ document.title = title;
16
+ },
17
+ [title]
18
+ );
19
+ ```
20
+
21
+ Destructure a primitive before putting it in an effect dependency array. Keep the returned query or route object intact
22
+ elsewhere so its namespace remains visible.
23
+
24
+ ## Dependencies
25
+
26
+ - **No objects or functions as dependencies (any version).** They are new each render, so the effect re-runs every time.
27
+ Create them inside the effect, move them outside the component, or depend on the primitive they read.
28
+ - **Hidden `<Activity>` cleans up effects (React 19.2+).** A hidden subtree unmounts its effects and re-runs them when shown.
29
+ Don't rely on an effect running while its UI is hidden.
30
+
31
+ See [Removing Effect Dependencies](https://react.dev/learn/removing-effect-dependencies).
32
+
33
+ ## Effect Events for values the effect should not react to
34
+
35
+ When an effect reads a value it should not react to, such as a callback prop or a setting used only once something happens,
36
+ move that logic into `useEffectEvent` (React 19.2+) instead of adding the value to the dependencies, mirroring it in a ref,
37
+ or suppressing the lint rule. Call the Effect Event only from inside the effect, and never use it to drop a value that should
38
+ re-run the effect. See [`useEffectEvent`](https://react.dev/reference/react/useEffectEvent).
39
+
40
+ ```tsx
41
+ const onConnected = useEffectEvent(function notifyConnected() {
42
+ if (!muted) toast("Connected");
43
+ });
44
+
45
+ useEffect(
46
+ function connectToRoom() {
47
+ const connection = createConnection(roomId);
48
+ connection.on("connected", onConnected);
49
+ connection.connect();
50
+
51
+ return function disconnectFromRoom() {
52
+ connection.disconnect();
53
+ };
54
+ },
55
+ [roomId]
56
+ );
57
+ ```
58
+
59
+ Read the React documentation when an effect decision is unclear.
@@ -0,0 +1,41 @@
1
+ # React state
2
+
3
+ Where each piece of state lives.
4
+
5
+ ## Put state where its truth lives
6
+
7
+ Choose the first suitable level:
8
+
9
+ 1. **URL state**: filters, sort, pagination, and active tab. Use TanStack Router search params and validate them.
10
+ 2. **Server state**: API data. Use TanStack Query; do not copy it into `useState`.
11
+ 3. **Subtree state**: state shared by a subtree and reset when it leaves. Use React context.
12
+ 4. **Client state**: app-wide persistent state. Use TanStack Store only when the earlier levels do not fit.
13
+ 5. **Local state**: one component's open state, draft, or input value. Use `useState`.
14
+
15
+ Shape state so it is not redundant or duplicated. Validate a form draft when it becomes a submitted domain value.
16
+
17
+ ## State shape
18
+
19
+ - **One status, not exclusive booleans (any version).** `status: "idle" | "saving" | "error"` instead of `isSaving` and
20
+ `isError` that can both be true. Merge state variables that always change together.
21
+ - **Reducers are pure (any version).** No requests, toasts, or timers inside a reducer. Dispatch one action per user
22
+ interaction, from the event handler.
23
+ - **Slow renders (React 18+).** Keep text input state out of a transition. When a slow list follows a prop or hook value,
24
+ render it from `useDeferredValue(value)`.
25
+
26
+ See [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure) and
27
+ [Extracting State Logic into a Reducer](https://react.dev/learn/extracting-state-logic-into-a-reducer).
28
+
29
+ ## Fallbacks without TanStack
30
+
31
+ TanStack Form and Query are the default. A project without them uses React's own tools:
32
+
33
+ - **Forms (React 19+):** `<form action={fn}>` needs no `preventDefault` and resets uncontrolled fields on success. Read
34
+ `useFormStatus` only in a child of the form. `useActionState` holds the result; dispatch it manually only inside an Action
35
+ or `startTransition`.
36
+ - **Optimistic updates (React 19+):** `useOptimistic`, with the setter called only inside an Action. Once the save succeeds,
37
+ the parent's value is the truth again.
38
+
39
+ See [`<form>`](https://react.dev/reference/react-dom/components/form),
40
+ [`useActionState`](https://react.dev/reference/react/useActionState), and
41
+ [`useOptimistic`](https://react.dev/reference/react/useOptimistic).
@@ -1,65 +1,12 @@
1
1
  # React
2
2
 
3
3
  Keep data flow one-way and components focused. Pages decide how data is loaded; presentational components decide how it
4
- looks.
4
+ looks. Read the subtopic that matches the task.
5
5
 
6
- ## Composition: pages (smart containers), partials, dumb components
6
+ | Subtopic | Read when |
7
+ | ----------------------------------- | --------------------------------------------------------------- |
8
+ | [composition](react/composition.md) | Deciding a component's role, or composing parts. |
9
+ | [state](react/state.md) | Deciding where state lives. |
10
+ | [effects](react/effects.md) | Writing or reviewing `useEffect`, or reading values in effects. |
7
11
 
8
- - **Pages** live in `routes/`. They load data, read URL state, coordinate actions, and compose the view. Route files hold the
9
- search schema, loaders, and pending, error, and not-found components. They do not own component CSS. Route-local views and
10
- forms live in the route's `-` folder, as [project-structure.md](project-structure.md) describes, and that folder's
11
- `.module.css` files are component styles.
12
- - **Partials** are compositions that more than one route uses, extracted because they read as a unit. They have light logic
13
- and no styling of their own.
14
- - **Layout components** arrange content with `Stack`, `Row`, grids, and token gaps, and wrap long-form text in `Prose`.
15
- - **Dumb components** receive data and callbacks, render UI, and own their styles. They do not fetch, route, or contain
16
- business rules.
17
-
18
- ```tsx
19
- function UserCard({ name, onEdit }: { name: string; onEdit: () => void }) {
20
- return (
21
- <Card.Root>
22
- <Typo.P>{name}</Typo.P>
23
- <Button onClick={onEdit}>Edit</Button>
24
- </Card.Root>
25
- );
26
- }
27
- ```
28
-
29
- ## Compose, don't configure
30
-
31
- Compose small parts instead of making one component configurable through many boolean props. Lift shared state to the nearest
32
- common parent.
33
-
34
- ## Put state where its truth lives
35
-
36
- Choose the first suitable level:
37
-
38
- 1. **URL state**: filters, sort, pagination, and active tab. Use TanStack Router search params and validate them.
39
- 2. **Server state**: API data. Use TanStack Query; do not copy it into `useState`.
40
- 3. **Subtree state**: state shared by a subtree and reset when it leaves. Use React context.
41
- 4. **Client state**: app-wide persistent state. Use TanStack Store only when the earlier levels do not fit.
42
- 5. **Local state**: one component's open state, draft, or input value. Use `useState`.
43
-
44
- Shape state so it is not redundant or duplicated. Validate a form draft when it becomes a submitted domain value.
45
-
46
- ## Effects: last resort, and named
47
-
48
- Use `useEffect` only to synchronize with something outside React. Do not use it for derived values, event responses, or
49
- server sync.
50
-
51
- A genuine effect belongs in a named custom hook. If it stays inline, name the callback:
52
-
53
- ```tsx
54
- useEffect(
55
- function syncDocumentTitle() {
56
- document.title = title;
57
- },
58
- [title]
59
- );
60
- ```
61
-
62
- Destructure a primitive before putting it in an effect dependency array. Keep the returned query or route object intact
63
- elsewhere so its namespace remains visible.
64
-
65
- Read the React documentation when a decomposition or effect decision is unclear.
12
+ Read the React documentation when a decomposition decision is unclear.
@@ -59,4 +59,5 @@ Use `.catch()` for malformed values that can safely fall back. Use `.default()`
59
59
 
60
60
  Build forms with TanStack Form and validate user input in the form's validators with the Zod schema, so a field error and the
61
61
  submitted value's type come from one schema. Field values held in `useState` and checked by hand with `safeParse` are a gap,
62
- even when the project's other forms do the same.
62
+ even when the project's other forms do the same. A project without TanStack Form follows the fallback in
63
+ [react/state.md](react/state.md#fallbacks-without-tanstack).