Front-end development

Junior Devs Should Audit 6 Risks in Any UI Framework

A UI framework can look excellent in a starter project and become expensive at the first ordinary change request. If you have a year or two of experience, you may be asked to recommend one before you have maintained one. My position: evaluate a frontend vendor, library, or framework by making it absorb a realistic change, not by counting features or stars.

A starter screen cannot tell you what a change will cost

Ask your team for a small, approved user-interface change that has already happened. Good candidates include adding a field that appears only for certain users, changing validation after an API response, or letting someone resume an unfinished form. Rebuild the original screen with each candidate, then apply the change. The second step is the evaluation: a tool that makes the first screen quick may make a routine revision awkward because its state model, component boundaries, or styling rules resist the new requirement.

Read Scope creep starts after the UI framework choice, not before; then check your dated requests before blaming a framework for added work. I would not treat every post-selection request as scope creep, because some requests were already implicit in the approved behavior and merely surfaced during implementation. Conversely, a library that requires a rewrite for a modest, clearly new request has exposed a cost worth recording. Keep those two findings separate in your notes.

Give the trial a planning cap of 6 hours per candidate; that is a budget to tune to your project, not a claim about how fast developers ought to work. Before coding, write down the same acceptance criteria for every option: the screen loads, a conditional field survives a page refresh, validation messages can be reached by keyboard, and an existing Playwright test still passes. Record the commit where the change begins. Afterwards, inspect the diff rather than relying on how the coding session felt. A change across a component, its test, and a type definition is different from one that also requires replacing the routing and form setup.

Use TypeScript to expose integration friction, rather than turning its checks off to finish the trial. If an option needs any at the boundary between your API response and the form, note which missing type or conversion caused it. Check the result against WCAG 2.2 keyboard and error-identification requirements, because a visually correct component can still make the requested change unusable. Neither TypeScript nor an accessibility checklist chooses the framework for you; both reveal work that a polished demo can hide.

Your most useful number may be local. If a stopwatch shows that the change took 47 minutes in one trial, label that as an observed result from your machine and your familiarity with the code. Do not present it as a universal benchmark. Save the commit, acceptance criteria, and test output alongside the time so another developer can explain why a second trial differs.

A release history is more useful than a vendor roadmap

Before trusting a library’s feature list, examine how its maintainers handle unwanted change. Read its release notes, migration guides, license, issue tracker, and security-advisory process. For a commercial component vendor, ask who supplies fixes when a supported framework version changes and whether you retain the right to use installed versions after cancellation. A roadmap is a statement of intent; a dated migration guide and a resolved compatibility issue show what customers actually had to do.

Read The Hidden Upkeep of Frontend Frameworks for Backend Devs; then inspect your own lockfile before assigning an upkeep estimate. I disagree with treating “framework upkeep” as one undifferentiated bill, because a dependency with frequent harmless patches imposes a different burden from a major release that forces changes to tests, build configuration, and application code. Record those jobs separately so your estimate can be challenged.

In an existing npm project with a build script and Playwright tests, this short check gives you evidence to discuss. Run it on a branch: the audit can report findings, and Playwright can fail for reasons you will need to investigate.

npm ci
npm run build
npm audit --omit=dev
npm outdated || true
npm ls --depth=0
npx --no-install playwright test
du -sh node_modules
git diff -- package-lock.json

The --omit=dev audit checks production dependencies, so it does not excuse you from reviewing build-tool advisories separately. npm outdated shows available versions, not whether upgrading is safe. The node_modules directory size is an installation observation, not a browser-download metric; use the Vite 7 build output and a browser check for delivered assets instead. These distinctions matter because a scorecard built from the wrong metric can make the wrong candidate look cheap.

Choose an upkeep window of 30 days as an initial review interval, and adjust it if your team ships or updates less often. If Renovate or Dependabot opens upgrade pull requests, inspect what those requests change: lockfile only, application code, snapshots, or Playwright tests. Note whether a maintainer publishes a tested upgrade path for your version of React, Lit, TypeScript, or Vite. A library with fewer releases is not automatically cheaper, because an unsupported dependency can leave your team doing the compatibility work itself.

For a paid vendor, request a written answer to a concrete scenario: “Our framework receives a major release; which versions of your components will you test, by when, and under which support terms?” Also ask for the renewal price and the process for exporting custom themes or data. I would not accept “enterprise-ready” as an answer, because it tells you neither who performs the migration nor what happens if the contract ends.

React 19 and Lit 3 win different trials

Compare React 19 with Vite 7 against Lit 3 with Vite 7 for a server-rendered application that needs interactive screens. This is a fairer comparison than building a whole application in one and a button in the other. Give both the same API response, visual design, accessibility criteria, and change request. Do not add a form library to only one candidate; that would measure your package choices as much as the UI approach.

React wins when the change involves coordinated state across several screens, because its component model and broad testing ecosystem give your team established ways to share and test that state. Its cost is that you must decide how to handle client rendering or hydration, dependencies, and boundaries between server output and React-owned markup. Lit wins when you need a few interactive elements inside pages that remain mostly server-rendered, because custom elements can be placed without making React the owner of the surrounding page. Its cost is the work of defining property and attribute behavior, testing form participation, and documenting how non-Lit code uses those elements.

Those are hypotheses, not verdicts. If your application already has React components and developers who know them, a Lit widget may increase integration work rather than reduce it. If your backend renders nearly every page and only a date picker needs client-side behavior, introducing a React root may add ownership and hydration questions without helping the change request. Count what you have to write and maintain in each actual setting.

Measure browser behavior with the same test device and network conditions for both candidates. Google publishes 200 milliseconds as the upper boundary of a “good” Interaction to Next Paint result; use that published threshold as context, not as a promise that either library will meet it. Lighthouse CI can catch build-to-build changes, while field Core Web Vitals data tells you how real visits behave when you have enough traffic. A faster synthetic run on your laptop does not prove that users on slower devices will see the same difference.

Check one more boundary before choosing: can a developer remove the candidate without rewriting unrelated pages? For Lit, try replacing a custom element with server HTML and a small script. For React, try replacing one isolated root without disturbing its neighbors. This is not an argument that removal will happen soon. It is a way to identify how much of the application your decision will claim, which makes future estimates more honest.

A scorecard should be able to reject your favorite

Write the scoring rules before you run the trial. Otherwise, it is easy to give extra weight to whichever result supports the tool you already know. Put acceptance tests first: if keyboard interaction fails or the changed screen cannot be built reliably in CI, the candidate does not pass yet. Then compare change effort, upgrade evidence, delivered JavaScript, and vendor terms. Keep raw results next to scores so a teammate can challenge a judgment without rerunning everything.

Set a provisional limit of 70 KB of additional compressed JavaScript for this screen, then tune that limit to your users and existing bundle. Call it a decision threshold, not an industry benchmark. Record whether the number comes from Vite output or from transferred bytes in the browser; those answer different questions. Likewise, count a Playwright failure as evidence to investigate, not proof that the framework is defective, because an incorrect test can fail in both candidates.

Include a “cannot verify” column. If a vendor will not clarify support terms, or a library has no documented answer for your required browser behavior, do not invent a reassuring score. Ask a senior developer to review the uncertainty and the affected requirement. That conversation is more valuable than presenting a neat ranking whose most consequential entries are guesses.

I would not select the candidate with the shortest first implementation unless it also survives the change request and the upkeep check. The initial screen is work you do once; revisions and dependency decisions recur. A recommendation grounded in a saved diff, passing tests, release evidence, and stated unknowns gives your team something concrete to accept or dispute.

Start with one dated request, not a new project

Tomorrow, find a completed UI change whose original request and final behavior you can both inspect. Write its acceptance criteria, create a branch, and reproduce it in one candidate before proposing a wider evaluation. Save the diff and test results. If the trial exposes a question you cannot answer, bring that specific question to a teammate rather than filling the gap with confidence.