> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/preactjs/preact/llms.txt
> Use this file to discover all available pages before exploring further.

# act

> Flush effects and renders in test code

The `act` function wraps test code that renders or updates components, ensuring all effects and renders are flushed before continuing.

## Signature

```typescript theme={null}
function act(callback: () => void | Promise<void>): Promise<void>
```

<ParamField path="callback" type="() => void | Promise<void>" required>
  The test code to run. Can be synchronous or return a Promise.
</ParamField>

Returns a Promise that resolves when all effects and renders have been flushed.

## How it works

From [test-utils/src/index.js:27-112](https://github.com/preactjs/preact/blob/main/test-utils/src/index.js#L27-L112):

1. Calls `setupRerender()` to enable synchronous rendering
2. Overrides `options.requestAnimationFrame` to capture effect callbacks
3. Executes your callback
4. Flushes all pending renders
5. Flushes all pending effects
6. Repeats until no more work is pending
7. Calls `teardown()` to restore options

The function supports nested calls and both sync and async callbacks.

## Usage

### Basic usage

```javascript theme={null}
import { render } from 'preact';
import { act } from 'preact/test-utils';

test('renders component', async () => {
  const container = document.createElement('div');
  
  await act(() => {
    render(<MyComponent />, container);
  });
  
  expect(container.textContent).toBe('Hello World');
});
```

### Testing state updates

```javascript theme={null}
import { useState } from 'preact/hooks';
import { render } from 'preact';
import { act } from 'preact/test-utils';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(c => c + 1)}>+</button>
    </div>
  );
}

test('updates on click', async () => {
  const container = document.createElement('div');
  
  await act(() => {
    render(<Counter />, container);
  });
  
  expect(container.querySelector('span').textContent).toBe('0');
  
  await act(() => {
    container.querySelector('button').click();
  });
  
  expect(container.querySelector('span').textContent).toBe('1');
});
```

### Testing effects

```javascript theme={null}
import { useEffect, useState } from 'preact/hooks';
import { render } from 'preact';
import { act } from 'preact/test-utils';

function DataLoader({ url }) {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetch(url).then(r => r.json()).then(setData);
  }, [url]);
  
  return <div>{data ? data.title : 'Loading...'}</div>;
}

test('loads data', async () => {
  const container = document.createElement('div');
  
  // Mock fetch
  global.fetch = () => Promise.resolve({
    json: () => Promise.resolve({ title: 'Test' })
  });
  
  await act(async () => {
    render(<DataLoader url="/api/data" />, container);
    await new Promise(r => setTimeout(r, 0));
  });
  
  expect(container.textContent).toBe('Test');
});
```

### Async callbacks

```javascript theme={null}
test('handles async updates', async () => {
  const container = document.createElement('div');
  
  await act(async () => {
    render(<MyComponent />, container);
    await someAsyncOperation();
    // More updates...
  });
  
  // All updates are flushed
  expect(container.textContent).toBe('Updated');
});
```

## Implementation details

From [test-utils/src/index.js:27-112](https://github.com/preactjs/preact/blob/main/test-utils/src/index.js#L27-L112):

```javascript theme={null}
export function act(cb) {
  if (++actDepth > 1) {
    // Nested calls: just execute callback
    try {
      const result = cb();
      if (isThenable(result)) {
        return result.then(
          () => { --actDepth; },
          e => { --actDepth; throw e; }
        );
      }
    } catch (e) {
      --actDepth;
      throw e;
    }
    --actDepth;
    return Promise.resolve();
  }

  const previousRequestAnimationFrame = options.requestAnimationFrame;
  const rerender = setupRerender();
  let flushes = [];

  // Capture effect callbacks
  options.requestAnimationFrame = fc => flushes.push(fc);

  const finish = () => {
    try {
      rerender();
      while (flushes.length) {
        let toFlush = flushes;
        flushes = [];
        toFlush.forEach(x => x());
        rerender();
      }
    } finally {
      teardown();
    }
    options.requestAnimationFrame = previousRequestAnimationFrame;
    --actDepth;
  };

  let result = cb();
  
  if (isThenable(result)) {
    return result.then(finish);
  }
  
  finish();
  return Promise.resolve();
}
```

<Note>
  The function uses a depth counter to handle nested `act()` calls. Only the outermost call performs flushing.
</Note>

## Best practices

### Always await act

```javascript theme={null}
// ✅ Good
await act(() => {
  render(<Component />, container);
});

// ❌ Bad - effects may not be flushed
act(() => {
  render(<Component />, container);
});
```

### Wrap all renders and updates

```javascript theme={null}
// ✅ Good
await act(() => {
  render(<Component />, container);
});

await act(() => {
  button.click();
});

// ❌ Bad - update may not flush
await act(() => {
  render(<Component />, container);
});
button.click(); // Not wrapped!
```

### Use with testing libraries

```javascript theme={null}
import { render, fireEvent } from '@testing-library/preact';
// @testing-library/preact wraps act automatically
```

## Related

* [setupRerender API](/api/test-utils/setup-rerender)
* [Testing guide](/advanced/testing)
* [useEffect API](/api/hooks/use-effect)
