Start several paths from one browser
Fork ordered Playwright sessions from one checkpoint and verify every child inherited the captured state.
Suppose a workflow has already reached an expensive state: it authenticated, loaded data, and filled part of a form. Several independent jobs now need to start from exactly there. Fork captures that common starting point once and creates peer browser sessions from it.
The state model
- The source prepares
"checkpointed"and captures it. - The source then changes its own value to
"source-after-checkpoint". - Every child should still contain
"checkpointed". - Children are independent sessions; changes in one do not appear in another.
1. Capture the shared starting point
The post-checkpoint edit is deliberate. It proves the children inherit the checkpoint, not whatever state happens to be live in the source later.
await page.goto(url);
await page.evaluate(
([key, value]: readonly [string, string]) =>
localStorage.setItem(key, value),
[markerKey, "checkpointed"] as const,
);
const checkpoint = await brawsr.createCheckpoint(session.id, {
label: `playwright-fork-${n}`,
});
await page.evaluate(
([key, value]: readonly [string, string]) =>
localStorage.setItem(key, value),
[markerKey, "source-after-checkpoint"] as const,
);2. Create ordered children
n comes from BRAWSR_FORK_N. With the default three-session organization
limit, use one or two children because the source session remains active too.
The lineage check confirms the children are visible from their source.
forked = await brawsr.fork(session.id, checkpoint, { n });
const lineage = await brawsr.getLineage(session.id);
if (lineage.children.length !== n)
throw new Error("fork children are missing from session lineage");3. Attach to each child and verify inheritance
Each child carries its own CDP connection. The branch index gives stable order; the marker check proves every browser started from the checkpointed state.
const restored = await Promise.all(
forked.children.map(async (child) => {
const connection = brawsr.connectCDP(child);
const browser = await chromium.connectOverCDP(connection.endpointUrl, {
headers: connection.headers,
});
browsers.push(browser);
const childPage = browser.contexts()[0]?.pages()[0];
if (!childPage)
throw new Error(`child ${child.branchIndex} has no restored page`);
return childPage.evaluate(
(key: string) => localStorage.getItem(key),
markerKey,
);
}),
);
if (restored.some((value) => value !== "checkpointed"))
throw new Error("fork lost browser state");4. Own the cleanup policy
brawsr does not decide which peers your application should keep. The example
closes every browser handle, every child session, and the source in finally.
await Promise.allSettled(browsers.map((browser) => browser.close()));
await closeSessionTree(brawsr, session.id, [forked]);View the complete Playwright fork example (opens in a new tab)