This post starts the Essential Skills series (4 posts in total).
If you are early in the job, you need names. You cannot aim at a skill you have never heard of. Roadmap.sh is still the best public map of those names. Use it.
What it cannot tell you is the altitude. A mid who can list Vite, Playwright, TypeScript generics, and a state library is still a mid if the work they own is a ticket. Senior is the size of the thing you can be handed, and what happens to it.
This post is both: the landscape you will actually meet, and what “good” looks like at each layer. I have written about how I measure that from the other side of the table. This series is the craft side. The next two cover the other side of the contract and when the job becomes other people. The last part is what generation being cheap did to the review job.
Names rotate. The layers do not. If a tool below has already been replaced on your team, learn the replacement — do not wait for this list to update.
How to read the lists
Treat every section as three altitudes of the same skill:
- Junior. You can use the tool on a well-defined piece with help. You know the name, you have run it, you ask instead of stalling.
- Mid. You ship a feature-sized piece in this layer without being walked through it. You know the happy path and the common failure.
- Senior. You decide whether this layer is the right one, what it costs, and what the team will live with. You can work when the documented approach is not the answer.
Memorizing the bullet list is the junior job. Reciting framework internals is a credibility check, not the senior one. The genre that separates mid from senior is the production corner case.
Language and types
JavaScript is still the language of the browser. TypeScript is the language of almost every serious product frontend I see.
What you will meet:
- Modern JavaScript (ES2015 and after): modules, destructuring, rest/spread, promises,
async/await, iterators, optional chaining, nullish coalescing. - The runtime underneath: the event loop, microtasks versus macrotasks,
thisbinding, closures, prototypes, howthisandasyncsurprise people in production. - TypeScript: interfaces and type aliases, unions and intersections, generics, utility types (
Partial,Pick,Omit,Record), narrowing, discriminated unions,unknownversusany, declaration files,tsconfig(strict,paths,noUncheckedIndexedAccess). - Modules and packages: ESM versus the CJS you will still find,
package.jsonexports, peer dependencies.
What senior looks like. You are not looking up async/await. You can explain why a type failed at an API boundary, why any leaked through a mapper, and why a “simple” promise chain raced. You can add TypeScript to a JavaScript island without boiling the ocean. Syntax recall is cheap now; reasoning from the runtime is not.
Frameworks
You need one framework deeply, and enough of a second that you can tell a library opinion from a web constraint.
What you will meet:
- Angular — components, standalone APIs, dependency injection, routing, forms (template and signal forms), RxJS where it still owns async, signals and
resource/httpResourcewhere the team has moved. This is my home stack; the rest of the site goes deep here. - React — function components, hooks, context, the current data libraries (TanStack Query is the one I see most), server components if the team is on a React meta-framework.
- Vue — Composition API, Pinia, Vue Router. You will meet Vue even if you never take a Vue job; enough teams use it that “I have never opened a
.vuefile” is a gap, not a virtue. - Meta-frameworks you will hear in the same breath: Next.js, Nuxt, Analog, Remix / React Router. They change where the code runs. They do not replace the platform.
What senior looks like. You can see the browser through the framework: change detection or the equivalent scheduler, what a re-render costs, when a “framework feature” is just the DOM. You can pick up the team’s stack in days, not months, because you already know the platform. Reciting how the scheduler works is a mid-plus quiz. Owning a production corner case the docs do not cover is the job.
You do not need all three. You need one as a home, and honesty about the others.
CSS and the layout you actually ship
The UI is still CSS. Frameworks do not save you from it.
What you will meet:
- The cascade, specificity, inheritance, writing modes. Logical properties (
inline-size,inset-block) if you ship RTL — I do. - Flexbox and Grid as the default layout tools. Positioning, stacking contexts, containing blocks.
- Responsive work: media queries, container queries, fluid type, viewport units and their mobile traps (
100vhversusdvh). - Modern CSS you will keep meeting: custom properties,
:has(), layers (@layer),color-mix(), subgrid where support exists. - Preprocessors and utilities: Sass on older codebases, Tailwind or a design-system token layer on newer ones, Bootstrap still on a surprising number of apps.
- Animation: transitions,
@starting-style, a little Web Animations API. Reach for a library (GSAP, Angular animations) when the CSS model is the wrong one.
What senior looks like. You can explain why a “simple” spacing change moved something three components away. You know when a utility class is faster than a new abstraction and when it has become a pile. You treat RTL, zoom, and long translations as part of the layout, not as a patch.
State and data on the client
State management is not a library decision. It is an answer to where the truth lives, who can change it, and what it costs to be wrong. You still need to know the libraries, because that is how teams talk.
What you will meet:
- Server / async state: TanStack Query, Angular
httpResource/resource, Apollo Client, RTK Query, SWR. Caching, invalidation, and “we are lying about the server for 200ms” live here. - Client / UI state: framework primitives first — Angular signals, React
useState/useReducer, Vueref/reactive. Then a store when the tree is the wrong place: NgRx or a signal store in Angular, Redux Toolkit or Zustand in React, Pinia in Vue. - URL state: the router is a store. Query params and path params are part of the model, not an afterthought.
- Local persistence:
sessionStorage/localStorage/ IndexedDB, with a clear rule about what may live there (hint: not tokens if you can help it). - Older names you will still see in reviews and job posts: Redux (classic), MobX, Vuex, Akita, NgXs.
What senior looks like. You can point at a screen and say: this is derived, this is a user decision, this is a server fact, this is a cache we accepted. You notice when a working change makes the next change harder. The wrong conversation is Redux versus signals. The right one is the shape.
If you cannot explain the shape without naming a vendor, you do not own the shape yet. Learn the vendors anyway — that is how you join the conversation.
Testing and debugging
What you will meet:
- Unit and component: Vitest or Jest, Testing Library (or the Angular equivalent), Cypress Component / Playwright component tests on some teams. Jasmine and Karma still exist on older Angular repos.
- End-to-end: Playwright is the one I want on a new repo. Cypress is everywhere. Selenium shows up in older suites and in enterprise constraints.
- What to test: a strategy that scales — not a coverage number. Unit for logic, component for UI contracts, e2e for the few paths that are the product.
- Debugging: browser DevTools (Performance, Network, Application, Accessibility), source maps that actually work, Angular DevTools / React DevTools / Vue DevTools, logging that you can find again.
What senior looks like. Writing tests is the mid altitude. Deciding what deserves which kind of test is the senior one. A suite that is slow, brittle, and proud of 90% coverage is not a strategy. An e2e that asserts a CSS class is theater. You also debug without guessing: reproduce, form a hypothesis, change one thing.
Build, packages, and the repo
What you will meet:
- Package managers: npm, pnpm, Yarn. Corepack pins the one the repo meant. Workspaces / monorepos: pnpm workspaces, npm workspaces, Nx, Turborepo.
- Bundlers and dev servers: Vite and esbuild on anything started recently. Webpack on everything that has been alive since 2018. rspack / Rolldown as the names replacing Webpack in conversation. Parcel still appears.
- Tasks:
package.jsonscripts. Nx or Turborepo pipelines in a monorepo. Gulp exists on legacy trees; you should be able to read it, not start a new one. - Quality gates: ESLint (or the team’s equivalent), Prettier or dprint, Stylelint, Husky / lint-staged or a CI-only check. Typecheck in CI, not only on the laptop.
- Git: branching, rebase versus merge, bisect, stash, worktrees, a pull-request habit that does not rewrite shared history by accident.
What senior looks like. You can change the pipeline without folklore. You know why the install is slow, why CI is red only on main, and which devDependency is doing surprising work at postinstall. You do not need to be the Webpack config person. You need to not be afraid of the file.
Performance
What you will meet:
- Measurements: Lighthouse, Web Vitals (LCP, INP, CLS, TTFB), Chrome Performance panel, a Real User Monitoring hook if the company has one (often SpeedCurve, Datadog RUM, or something home-grown).
- Usual levers: code splitting and lazy routes, image formats and
srcset, fonts that do not block, bundle analysis (rollup-plugin-visualizer, webpack-bundle-analyzer, source-map-explorer), tree-shaking that actually works, fewer layout-forcing reads. - Runtime: virtualization for long lists, debounce/throttle where they belong, workers for CPU work that janks the main thread, caching and prefetch.
What senior looks like. Everyone knows lazy loading. A senior has a process when the obvious techniques do not apply: measure, hypothesize, change one thing, measure again. Lighthouse is a flashlight. It is not a personality. You also know when “faster” is the wrong target — a 20ms win that made the next feature impossible is not a win.
Accessibility
This is not a polish pass and it is not only a legal footnote. It is part of the interface you shipped.
What you will meet:
- WCAG 2.2 as the language auditors and designers use (A / AA; AAA is rare in product UI).
- Semantic HTML first. ARIA when the native element was the wrong one — roles, names, states, live regions. The ARIA Authoring Practices for the widgets HTML does not give you.
- Keyboard paths, focus management, skip links, focus visible.
- Contrast, zoom to 200%, reduced motion, screen readers (VoiceOver, NVDA) at least enough to fail a flow yourself.
- Tooling: axe / axe DevTools, Lighthouse a11y, eslint-plugin-jsx-a11y or angular-eslint a11y rules, storybook-addon-a11y.
What senior looks like. You do not sprinkle aria-label on a <div> that should have been a <button>. You treat keyboard, name, and contrast as you treat the happy-path click. You know when a scanner is right and when it is noisy.
Talking to APIs
What you will meet:
- REST as the default: resources, status codes, pagination, idempotency, error bodies that the UI can actually branch on.
- GraphQL where over-fetching was a real problem: queries, mutations, fragments, a client (Apollo, urql, Angular’s
apollo-angular). Subscriptions if the product is live. - The boring parts that decide whether the UI is honest: auth headers versus cookies, CSRF, uploads, retries, timeouts, cancellation (
AbortController), optimistic updates you can roll back.
What senior looks like. A mid consumes the contract. A senior influences it before it ships — what “empty” means, which field is the identity, what a 409 looks like — because they will live with the lie. GraphQL is a tool. It is not required to be senior, and it is not a substitute for understanding HTTP.
Delivery and production
You do not become senior by collecting CI logos. You become senior when the path from your laptop to a user is something you can reason about.
What you will meet:
- CI/CD: GitHub Actions is the default I see. GitLab CI, CircleCI, Jenkins, Azure Pipelines on teams that already had them. Travis is legacy.
- Preview and release: Vercel / Netlify / Cloudflare Pages for some apps, an internal preview environment for others, feature flags (LaunchDarkly, Unleash, a home-grown flag).
- Containers: enough Docker to read a
Dockerfileand a compose file. Kubernetes is a platform skill, not a frontend essential — know what a pod and a rollout are so you can talk to the people who own them. - Observability: browser errors (Sentry, Datadog, Grafana Faro), logs you can search, a dashboard that is not only backend RED metrics. I have written about frontend observability on the Systems path if you want the deep end.
Frontend delivery also accumulates authority — install hooks, CI identities, release credentials. I wrote about why the system that produces the bundle is privileged. You do not need to run the platform team. You do need to stop pretending the frontend ends at ng serve.
Security you cannot leave to “the backend”
What you will meet:
- XSS and how your framework claims to stop it — and the
innerHTML,bypassSecurityTrust*, andmarkdownpipes that undo that claim. - CSRF, cookie flags (
HttpOnly,Secure,SameSite), where tokens live. - Auth patterns: session cookies and a BFF versus tokens in JS, OIDC (Auth0, Keycloak, Entra ID), route guards that are UX, not security.
- Dependency risk: lockfiles,
npm audit/ OSV, Dependabot or Renovate, the install hook you did not read.
What senior looks like. You reason about browser and delivery risk, not only “we should sanitize input.” A mid names OWASP. A senior can point at the specific place this app is exposed.
Collaboration
What you will meet:
- Pull requests and review culture. Conventional comments, suggested changes, “this is blocking” versus “this is taste.”
- Docs: a README that can bootstrap a machine, ADRs for the decisions you will forget, JSDoc / API docs for the shared layer, Storybook or an equivalent for the components other people will misuse.
- Designers: Figma (or whatever the file is), tokens, the gap between a spec and a state machine.
- Backend and QA: the contract conversation above, a shared language for bugs.
What senior looks like. A review that finds working-but-wrong and still handles the person who wrote it. A standard that exists outside your head. Other people get faster because you were in the room. Formal leadership is a different job — that is the team lead post.
Soft skills that are not decoration
These are not a second essay. They are how the technical list actually ships.
- Communication. You can explain a tradeoff to a designer, a backend, and a product person without three different lies. Written updates that a future you can read.
- Problem-solving. You diagnose before you rewrite. You can say what you tried and what would change your mind.
- Local leadership. Mentoring a junior through a review, running a small design conversation, making an implicit rule explicit. Growing people without a title.
If you want the deep treatments, they live later on this path: conflict, translation, mentorship.
A map, not a verdict
You will have gaps. After enough years in one stack, the gaps are often in the peripheral toolchain that matured while you were shipping — I called that the ecosystem blind spot. Use structured maps when you feel lost. Do not use them as a verdict on whether you are still senior.
Open roadmap.sh/frontend. If a bubble is a name you have never seen, that is a research topic, not a character flaw. If you can name every bubble and still only own tickets, the list is not the thing you are missing.
Where this post stops
This is the IC craft bar: language, framework, CSS, state, quality, delivery, security, and collaboration — with names, and with an altitude.
If the work you own crosses the contract — the API, the failure, the data — that is the full-stack post. If the job became other people’s work more than your own, that is the team lead post. And if the first draft is now cheap, the review job changed; that is the last part of this series.