React Adapter
April 4, 2026 · View on GitHub
Intercept fetch calls in React apps with Schmock. Works in both tests (Node/jsdom) and browser (dev-time).
bun install @schmock/react
Basic Usage
import { schmock } from '@schmock/core'
import { SchmockProvider } from '@schmock/react'
const mock = schmock()
mock('GET /api/users', [{ id: 1, name: 'Alice' }])
mock('POST /api/users', ({ body }) => [201, { id: 2, ...body }])
function App() {
return (
<SchmockProvider mock={mock}>
<YourApp />
</SchmockProvider>
)
}
SchmockProvider patches globalThis.fetch on mount and restores it on unmount. Any fetch() call inside the tree — whether from your code, React Query, SWR, or axios — is intercepted automatically.
Strict Mode: In React 18+ development mode, components mount → unmount → remount.
SchmockProviderhandles this correctly (it restores fetch on unmount and re-intercepts on remount), but there is a brief window between unmount and remount where fetch is unpatched. If you see intermittent failures in Strict Mode, this is why — they won't occur in production builds.
Options
<SchmockProvider
mock={mock}
options={{
baseUrl: '/api', // only intercept URLs starting with this prefix
passthrough: true, // pass unmatched routes to real fetch (default: true)
beforeRequest: (request) => ({
...request,
headers: { ...request.headers, 'x-tenant': 'dev' },
}),
beforeResponse: (response) => ({
...response,
headers: { ...response.headers, 'x-mock': 'true' },
}),
errorFormatter: (error) => ({
message: error.message,
timestamp: new Date().toISOString(),
}),
}}
>
<YourApp />
</SchmockProvider>
passthrough
When true (default), requests that don't match any Schmock route are forwarded to the real fetch. Set to false to return errors for unmatched requests — useful in tests to catch unexpected API calls.
baseUrl
Only intercept requests whose pathname starts with this string. Non-matching requests go straight to real fetch without being processed.
useSchmock Hook
Access the mock instance from any component inside the provider:
import { useSchmock } from '@schmock/react'
function DevTools() {
const mock = useSchmock()
return (
<div>
<p>Requests: {mock.callCount()}</p>
<button onClick={() => mock.resetHistory()}>Clear</button>
</div>
)
}
Throws if used outside a SchmockProvider.
Testing
With SchmockProvider directly
import { render, screen, waitFor } from '@testing-library/react'
import { schmock } from '@schmock/core'
import { SchmockProvider } from '@schmock/react'
it('loads users', async () => {
const mock = schmock()
mock('GET /api/users', [{ id: 1, name: 'Alice' }])
render(
<SchmockProvider mock={mock}>
<UserList />
</SchmockProvider>
)
await waitFor(() => {
expect(screen.getByText('Alice')).toBeDefined()
})
expect(mock.called('GET', '/api/users')).toBe(true)
})
With renderWithSchmock shorthand
A convenience wrapper that creates the mock, registers routes, and wraps your component:
import { renderWithSchmock } from '@schmock/react/testing'
it('loads users', async () => {
const { mock } = renderWithSchmock(<UserList />, {
routes: [
['GET /api/users', [{ id: 1, name: 'Alice' }]],
],
})
await waitFor(() => {
expect(screen.getByText('Alice')).toBeDefined()
})
expect(mock.callCount()).toBe(1)
})
renderWithSchmock returns the standard @testing-library/react RenderResult plus a mock property for assertions.
Note:
renderWithSchmockrequires@testing-library/reactas a peer dependency. It is exported from@schmock/react/testing(a separate entry point) so projects that don't use Testing Library are not affected.
Stateful Mocking
Combine with Schmock's state management for realistic CRUD flows:
const mock = schmock({
state: { users: [{ id: 1, name: 'Alice' }], nextId: 2 },
})
mock('GET /api/users', ({ state }) => (state as any).users)
mock('POST /api/users', ({ body, state }) => {
const s = state as any
const user = { id: s.nextId++, ...body as object }
s.users.push(user)
return [201, user]
})
mock('DELETE /api/users/:id', ({ params, state }) => {
const s = state as any
s.users = s.users.filter((u: any) => u.id !== Number(params.id))
return [204, null]
})
render(
<SchmockProvider mock={mock}>
<UserManager />
</SchmockProvider>
)
Helper Functions
Response helpers are available from @schmock/core:
import { notFound, badRequest, created, noContent } from '@schmock/core'
mock('GET /api/users/:id', ({ params, state }) => {
const user = state.users.find(u => u.id === Number(params.id))
return user || notFound('User not found')
})
mock('POST /api/users', ({ body }) => {
if (!body?.name) return badRequest('name is required')
return created({ id: 3, ...body })
})
mock('DELETE /api/users/:id', () => noContent())