Front-end work often looks scalable until two teams change the same customer journey in one release. I would scope state ownership and cross-team regression work before funding a larger component library, because the first failure is usually an inconsistent screen, not an inability to render one. For a product manager, that distinction changes both the estimate and the acceptance criteria.
The first thing to break is the ownership of a customer state
A component can be shared while the experience it produces remains inconsistent. Imagine a catalog page with a category filter. One team stores the selection in the URL so a customer can share it; another stores it only in React state so the page feels responsive. Both implementations may use the same filter component, yet a reload can clear the selection on one page and preserve it on another. The visible defect sits between routing, data fetching and product behavior, so polishing the component cannot settle it.
This is why I would begin with a state-owner map rather than a component inventory. For each important journey, name the source of truth for the URL, server response, local draft and persisted preference. Then name who decides what happens when those sources disagree. A filtered page opened from an old bookmark is a better scoping example than a screenshot of the default page, because it exposes decisions about stale parameters, empty results and back-button behavior.
For planning, assume six teams touch a shared account journey during a quarter; that is a scenario to validate with your release calendar, not a measured industry average. Ask each team to identify the states it can change and the teams that consume them. If no one owns the transition from an expired session back to a saved draft, put that transition on the backlog as product work. Calling it “front-end cleanup” conceals the decision the engineers need.
Front-End Craftsmanship for Scalable Web Apps makes a useful case for deliberate implementation, but implementation discipline alone cannot assign the owner of a disputed state because the answer depends on the intended customer behavior. I would not commission a component-library rewrite first: it would replace markup and APIs before the teams agree on which behaviors those APIs must preserve. Keep components that work, and spend the initial budget on the journeys where ownership is contested.
A compact deliverable is a table with one row per consequential state transition: trigger, source of truth, expected visible result, error or recovery path, and decision owner. Its value is that a PM can approve a behavior without pretending to approve an implementation detail. Start with a proposed three journeys, chosen for concurrent team changes or costly customer failure; increase that number only when the release map shows a wider dependency.
A shared component spreads an undefined behavior faster than duplicated code
Shared UI is often sold as a way to make one fix everywhere. It also makes one ambiguous change everywhere: if a common form field starts clearing its value after server validation, every flow using it may inherit a recovery problem. The scope question is therefore not “How many components can we consolidate?” but “Which behaviors can every consumer safely inherit?” A button’s color is easy to share; the meaning of its loading, disabled and retry states may depend on the transaction around it.
Define a behavioral contract before declaring a component shared. For a form control, specify whether it owns its value or accepts one, how it associates a label and error using HTML and WAI-ARIA, and what happens when an asynchronous response arrives after the user edits again. WCAG 2.2 AA gives the team an external accessibility target, but it does not decide whether a late response should overwrite a newer draft. That remains a product decision because either outcome changes what the customer sees.
Front-End Craftsmanship Tips for Scalable UI Code is useful for improving code-level consistency, but I would not treat a clean component API as proof of safe reuse because consumers can depend on different state transitions. TypeScript can catch an invalid prop shape; it cannot determine whether a session-expired message should retain a submitted form. Storybook can display the retained-draft state, while a journey test must verify that the state survives the actual sign-in detour.
This changes how to estimate migration. Counting component instances gives a starting inventory, not a delivery estimate, because two instances can carry different validation and recovery obligations. For a proposed pilot, select two consumers with different flows—a simple settings form and a multi-step purchase form, for example. That number is a scope limit to tune, not a promise that the rest will be interchangeable. Record the behavior each consumer requires before estimating the remaining moves.
CSS cascade layers and design tokens can reduce styling conflicts when several teams contribute to one interface, because they make priority and shared values explicit. They cannot resolve competing product rules. If a design-system change requires exceptions in most consuming flows, pause the rollout and ask whether the component boundary is wrong; adding variants indefinitely makes future changes harder to reason about, even when each variant is individually tidy.
Regression coverage should follow transitions rather than pages
A page can pass a screenshot check while its most expensive failure remains invisible. A screenshot taken after a fresh load will not show that browser Back restores the wrong filter, that a retry submits twice, or that a draft disappears after authentication. For scope and acceptance, describe the transition: starting condition, user action, interruption, recovery and observable outcome. That description gives engineering a test target and gives the PM a way to reject an incomplete “done.”
Playwright is suited to a small set of browser-level transition checks because it exercises navigation and visible UI together. In an npm project, with the application running on port 3000 and a catalog route that honors its category query parameter, this runnable shell snippet checks one URL-to-UI contract:
npm install --save-dev @playwright/test
npx playwright install chromium
mkdir -p tests
cat > tests/catalog.spec.cjs <<'EOF'
const { test, expect } = require('@playwright/test');
test('filter survives reload', async ({ page }) => {
await page.goto('http://localhost:3000/catalog?category=shoes');
await page.reload();
await expect(page).toHaveURL(/category=shoes/);
await expect(page.getByRole('heading', { name: 'Shoes' })).toBeVisible();
});
EOF
npx playwright test tests/catalog.spec.cjs
That test is intentionally narrow: it checks the contract that a bookmark still produces the selected category, not every detail of the catalog. Add a separate case for a failed request if recovery matters to that journey. Mock the failure deterministically rather than waiting for a real outage, because an unreliable test will eventually be ignored. Use Storybook for isolated visual and interaction states, Playwright for critical transitions, and accessibility checks against WCAG 2.2 AA for barriers those tests might otherwise miss; each covers a different failure mode.
Performance needs its own field evidence, not a passing local build. Google’s published Core Web Vitals thresholds define a good Largest Contentful Paint at 2.5 seconds or less, Interaction to Next Paint at 200 milliseconds or less, and Cumulative Layout Shift at 0.1 or less, assessed at the 75th percentile. Those are published benchmarks, not measurements of your product. Use Chrome DevTools to investigate a slow interaction, and use field reporting or Lighthouse CI trends to decide whether a change made the shipped experience worse; a lab run alone cannot represent every customer device or network.
Do not turn those metrics into a blanket front-end rewrite request. If field data points to one slow checkout interaction, isolate its event handler, rendering work and network dependency first, because a broad rewrite changes more behavior than that evidence justifies. The PM’s acceptance criteria should name the affected journey and the evidence needed to close it.
A contribution contract beats a central approval queue when releases overlap
There are two credible ways to govern shared front-end work. A central component gate means a dedicated team reviews and often implements changes to shared UI. It wins when the surface is small and consistency risk is high, because a single owner can enforce decisions; it costs queue time and can delay product teams whose releases depend on that owner. A federated contribution contract lets product teams submit changes under documented API, accessibility and regression requirements. It wins when several teams release independently, because implementation need not wait in one queue; it costs review capacity, documentation and stronger automated checks.
I would choose the federated contract for a product with overlapping releases, provided someone still owns disputed behavioral decisions. Without that owner, federation merely distributes the argument. The contract should require a changed-state example, a list of affected consumers, a migration note when behavior changes, and evidence from the relevant transition tests. React 19 and TanStack Query 5 can support a consistent implementation, but neither library defines which team may change a shared loading or retry rule.
For the estimate, separate work into discovery, contract decisions, pilot implementation, consumer migrations and release observation. This prevents a component estimate from silently absorbing product decisions and test construction. Budget an illustrative one release cycle to observe the pilot across its consumers before promising a wider migration; adjust that allowance to your actual release cadence and rollback options. If the pilot produces repeated exceptions, revisiting the boundary is cheaper than scaling the exception pattern.
Track two outcomes after launch: defects caused by inconsistent transitions and time spent waiting for shared-UI decisions. The first shows whether customers are getting coherent behavior; the second shows whether governance has become the bottleneck. Neither requires a new dashboard to begin—release tickets with the affected journey and decision owner can provide an initial record. Expand the process only when that record identifies a recurring failure worth preventing.
Start by finding the transition no team owns
At the next planning session, pick a journey scheduled for changes by more than one team and walk through a reload, a failed request and a return from sign-in. Write down the expected result and owner for each transition before estimating UI changes. If the group cannot agree, scope that decision explicitly. It is likely to block delivery sooner than another missing reusable component.


