# How tap differs from React
URL: /docs/tap/differences-from-react

The handful of behaviors that aren't quite React.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

Resources are React hooks, so almost everything carries over: the rules of hooks, dependency arrays, memoization, refs, and effect cleanup all work as you expect. This page covers the few places tap deliberately differs.

## Effects run in call order

In React, effects run children-first (inside-out), because a component can only reach its children by returning them. In tap, `useResource` is just another hook, so **effects run in the exact order they are called**, and you can place effects before or after a child.

```
import { resource, useResource } from "@assistant-ui/tap";
import { useEffect } from "react";

const useParent = () => {
  useEffect(() => console.log("1: before child"));
  const child = useResource(Child());
  useEffect(() => console.log("3: after child"));
  return child;
};

const Parent = resource(useParent);

const useChild = () => {
  useEffect(() => console.log("2: child"));
};

const Child = resource(useChild);
// Mount order: 1, 2, 3
```

Cleanup on unmount runs in the same order (FIFO). This lets a parent run setup both before and after its children, which is useful when it reacts to data a child provides.

## The tree re-renders from the root

This is the biggest difference. In React, a state change re-renders only the component that changed and its descendants. In tap, **the whole resource tree re-renders from the root**: because a parent reads its child's return value directly, the parent must re-run to receive the new value, and so must its parent, all the way up.

[`useTapRoot`](/docs/tap/trees-and-rerenders#separate-scheduling-with-usetaproot) breaks this chain by creating a new root with its own scheduler. Everything below it re-renders independently, and the parent does not re-render when that root updates.

## `useLayoutEffect` and `useInsertionEffect` collapse onto `useEffect`

tap has a single effect primitive. `useLayoutEffect` and `useInsertionEffect` are accepted (so React code ports cleanly) but behave like `useEffect` inside a resource.

## Where a throwing snapshot surfaces depends on the host

`useSyncExternalStore` matches React: when a store notifies and `getSnapshot` throws, the snapshot counts as changed and the resource re-renders. The error comes from that render, not from the store's notify loop.

Which render that is depends on the host at the root of the tree, not on the hook that reads the store. A tree owned by a React component ([`useResource`](/docs/tap/api-reference#useresource) called from a component, or [`useTapRoot`](/docs/tap/trees-and-rerenders#separate-scheduling-with-usetaproot), which escalates a throwing update into a host render) surfaces the error in a React render, where an error boundary can catch it. A bare [`createTapRoot`](/docs/tap/api-reference#createtaproot) re-renders in the scheduler flush, so the error is uncaught, the same route every other resource render error takes there.