JavaServer Faces served enterprise Java teams well for over a decade. It offered a component-based UI model that felt familiar to server-side developers, and it shipped with enough built-in functionality to build complex business applications without touching JavaScript. But in 2024, JSF is a liability. The framework has effectively reached end-of-life in terms of active development momentum. The talent pool of developers willing to work with JSF is shrinking every year. And the user experience expectations that modern customers have are simply not achievable within JSF's server-rendered, full-page-postback architecture.
If you are reading this, you probably already know your JSF application needs to migrate. The modern alternatives -- React 18, Angular 17, and Vue 3 -- have never been more capable. The question is how to make the transition without breaking the business logic that has been refined over years, without halting feature development for a year-long rewrite, and without demoralizing the team in the process. This playbook covers exactly that.
Why JSF Is Being Replaced
Before diving into migration strategies, it is worth understanding the specific forces that make JSF untenable for modern applications. This is not about chasing trends. These are real engineering and business constraints.
The architectural mismatch
JSF was designed around a server-centric model where the server manages UI component state, handles rendering, and processes every user interaction through full or partial page postbacks. Modern web applications require rich, interactive client-side experiences with instant feedback, offline capability, and seamless transitions. These are fundamentally incompatible with JSF's architecture.
- Server-side state management: JSF stores view state on the server (or serialized to the client), consuming memory and creating scalability challenges. Every user session holds a component tree in memory, which limits horizontal scaling.
- Tight coupling of UI and backend: In JSF, managed beans are directly bound to UI components. This makes it nearly impossible to reuse business logic across different clients (mobile apps, third-party integrations, partner APIs) without significant refactoring.
- Limited JavaScript ecosystem access: JSF's component model wraps HTML and JavaScript behind server-side abstractions. Integrating modern JavaScript libraries, design systems, or analytics tools requires fighting the framework rather than working with it.
- Development velocity: Hot reload, fast feedback loops, and component-driven development are standard in modern frontend frameworks. JSF requires server restarts for most changes and offers no equivalent developer experience.
The talent and ecosystem problem
The JSF ecosystem has contracted significantly. PrimeFaces and other component libraries still receive maintenance updates, but the pace of innovation has slowed to a crawl. Hiring developers who want to work with JSF is increasingly difficult. Junior and mid-level developers overwhelmingly prefer React, Angular, or Vue, and senior JSF specialists are either retiring or have already transitioned to modern stacks. This technical debt compounds over time, making every delayed migration more costly.
The business impact
JSF applications are slower to enhance, harder to staff, and increasingly expensive to maintain. Every year you defer migration, the gap between your application's user experience and your competitors' widens. Customers who interact with consumer-grade web applications daily will not tolerate sluggish, page-refresh-heavy enterprise UIs when alternatives exist.
Choosing Your Target Framework
The three dominant choices for replacing JSF are React 18, Angular 17, and Vue 3. Each has different strengths, and the best choice depends on your team's background, your application's complexity, and your long-term hiring strategy.
| Factor | React 18 | Angular 17 | Vue 3 |
|---|---|---|---|
| Learning curve for Java developers | Moderate | Low (TypeScript, DI, structure) | Low to moderate |
| Enterprise form handling | Good (React Hook Form, Formik) | Excellent (built-in reactive forms) | Good (VeeValidate, FormKit) |
| Opinionated architecture | Low (choose your own) | High (prescribed patterns) | Medium (flexible but guided) |
| Hiring pool (US/Europe) | Largest | Large (strong in enterprise) | Growing |
| Component library maturity | Extensive (MUI, Ant Design) | Strong (Angular Material, PrimeNG) | Good (Vuetify, PrimeVue) |
| Best fit for JSF migration | Teams wanting flexibility | Teams wanting structure | Small teams wanting speed |
For enterprise JSF applications with complex forms, strict architecture requirements, and Java-experienced teams, Angular 17 is the most natural fit. Its TypeScript-first approach, dependency injection system, and opinionated project structure feel familiar to Java developers. Angular 17's new standalone components as the default, improved control flow syntax, and signals system make it more approachable than earlier versions. React 18 is the safest long-term bet for hiring and ecosystem support. Vue 3 with the Composition API is the right choice when you need the smallest possible team to move the fastest.
The framework you choose matters less than the migration strategy you follow. A well-executed incremental migration to any of these frameworks will outperform a poorly planned rewrite to the "best" one. Pick the framework that your team can be productive in within two weeks, not the one that scores highest on a feature comparison chart.
Three Migration Strategies
There are three proven approaches to migrating from JSF to a modern frontend. Each carries different levels of risk, speed, and organizational impact. Understanding the trade-offs upfront prevents the most common migration failure: choosing a strategy that does not match your constraints.
Strategy 1: The strangler fig pattern
The strangler fig pattern is the most widely recommended approach for migrating legacy applications, and for good reason. Named after the tropical fig that gradually envelops its host tree, this strategy replaces the old application piece by piece while keeping the existing system fully operational.
Here is how it works for JSF:
- Deploy a reverse proxy (NGINX, Apache, or a cloud load balancer) in front of both your existing JSF application and your new frontend application.
- Route by URL. Initially, all requests go to the JSF application. As you build new pages in the modern framework, update routing rules to direct those specific URLs to the new frontend instead.
- Build new features exclusively in the modern framework. Every new page or feature goes into the new application. The JSF application receives only bug fixes and critical patches.
- Migrate existing pages incrementally. Prioritize the highest-traffic and highest-value pages first. Each migration delivers immediate value to users.
- Retire the JSF application once all pages have been migrated and the routing layer sends zero traffic to the old system.
- Best for: Applications with 50+ screens where a full rewrite is too risky. Teams that need to continue shipping features during migration.
- Risk level: Low. The existing application remains fully functional throughout.
- Timeline: 6 to 18 months depending on application size, with value delivered continuously.
Strategy 2: Micro-frontends
The micro-frontend approach extends the strangler fig concept by allowing multiple frontend frameworks to coexist within the same user experience. Instead of routing entire pages to different applications, you compose the page from independent frontend modules that can be built with different technologies.
Tools like Module Federation (available in both webpack 5 and the newer Rspack bundler), Single-SPA, or Web Components make this possible. Your JSF application can host embedded React or Angular components, and over time, the modern components take over more of the page until the JSF shell itself can be replaced.
- Best for: Large organizations with multiple teams working on different sections of the same application. Applications where different modules have different modernization priorities.
- Risk level: Medium. Adds architectural complexity around shared state, authentication, and styling consistency.
- Timeline: 6 to 24 months, depending on the number of teams and modules involved.
Strategy 3: Full rewrite
A full rewrite means building the entire new frontend from scratch in the modern framework, then switching over in one release. This is the approach that most teams instinctively want and the one that fails most often.
- Best for: Small applications with fewer than 20 screens. Applications where the existing UI is so fundamentally broken that incremental improvement is not practical. Teams that can dedicate a group to the rewrite without stopping feature development on the existing application.
- Risk level: High. The rewrite delivers zero value until completion. Scope creep is almost guaranteed. The legacy modernization playbook consistently shows that big-bang rewrites take 2 to 3x longer than initially estimated.
- Timeline: 6 to 12 months for small applications. 12 to 36 months for large enterprise systems (and often longer in practice).
If your JSF application has more than 30 screens, the strangler fig pattern is the right default choice. Full rewrites of large applications have a failure rate that should give any engineering leader pause. The incremental approach is slower on paper but dramatically faster in practice because it delivers working software from week one.
Building the Backend API Layer
The single most important technical step in a JSF migration is creating a clean API layer between your Java backend and the new frontend. In JSF, the managed beans talk directly to the UI components. In a modern architecture, the frontend communicates with the backend exclusively through REST or GraphQL APIs.
Extracting APIs from managed beans
Your existing JSF managed beans contain a mix of UI logic, business logic, and data access. The migration process requires separating these concerns:
- Identify the service layer. Most well-structured JSF applications already have EJBs, CDI beans, or Spring services behind the managed beans. If your managed beans call service classes directly, you are in good shape. If business logic lives inside the managed beans themselves, you need to extract it into service classes first.
- Create REST controllers. Using JAX-RS (Jersey, RESTEasy) or Spring MVC, build REST endpoints that expose the same operations your managed beans currently perform. Each endpoint should call the same service classes your managed beans use.
- Define clear DTOs. Do not expose your JPA entities directly. Create Data Transfer Objects that represent exactly what the frontend needs. This decouples your database schema from your API contract and prevents accidental data leaks.
- Handle authentication. JSF typically relies on container-managed security or server-side session authentication. Modern frontends use token-based authentication (JWT, OAuth 2.0). Implement a token-based auth layer that your new frontend can use while maintaining backward compatibility with the JSF session model during the transition.
Managing the dual-stack period
During migration, your JSF pages and your modern frontend pages both need to work simultaneously. This means both the server-side JSF session and the token-based API must share authentication state. The simplest approach is to have your authentication gateway issue both a session cookie (for JSF) and a JWT (for the new frontend) on login. As pages migrate, they switch from session-based to token-based calls transparently.
Preserving Business Logic
The cardinal rule of JSF migration is: do not rewrite business logic during a UI migration. Your backend business rules have been tested, refined, and validated by real users over years. Rewriting them simultaneously with the frontend introduces compounding risk that has derailed countless migration projects.
What moves and what stays
- Stays in Java: All business rules, validation logic, data transformations, workflow orchestration, and integration code. These remain in your service layer and are accessed through the new API endpoints.
- Moves to the frontend: UI state management, form rendering, client-side validation (duplicated from server-side for UX), navigation logic, and presentation formatting.
- Gets refactored: Managed bean code that mixes UI and business concerns. The business parts move into services. The UI parts are reimplemented in the modern framework.
This separation is where having experienced Java engineers matters. Understanding which code is business logic and which is JSF framework plumbing requires deep familiarity with both the application domain and JSF internals. If you need specialized Java talent for this phase, staff augmentation gives you access to engineers who have done this migration before without the overhead of permanent hires.
Incremental Migration: A Step-by-Step Process
Here is the concrete, week-by-week process for executing a strangler fig migration from JSF to a modern frontend. This assumes a mid-sized application with 50 to 100 screens and a team of 4 to 6 engineers.
Phase 1: Foundation (weeks 1 to 4)
- Set up the new frontend project with your chosen framework, build tooling, and CI/CD pipeline
- Deploy the reverse proxy routing layer
- Implement token-based authentication alongside the existing JSF session model
- Build the first 2 to 3 REST endpoints for a low-risk, well-understood page
- Migrate the first page as a proof of concept, deploy it to production behind the proxy
- Establish shared styling tokens and design patterns that both JSF and the new frontend can use to maintain visual consistency
Phase 2: Velocity (weeks 5 to 16)
- Migrate the highest-traffic pages first, typically 2 to 4 pages per sprint depending on complexity
- Build reusable component libraries for common UI patterns (data tables, forms, navigation)
- Expand the REST API surface area as each page migration requires new endpoints
- Run both versions of migrated pages in parallel for 1 to 2 weeks per page to validate data parity
- Collect user feedback on the modernized pages and iterate
Phase 3: Acceleration (weeks 17 to 30)
- The team has established patterns and velocity increases significantly
- Migrate remaining pages, prioritizing by business value and user impact
- Begin decommissioning JSF components that are no longer served to any users
- Optimize the API layer based on real frontend consumption patterns (aggregation endpoints, caching, pagination)
Phase 4: Completion (weeks 31 to 40)
- Migrate the final long-tail pages (admin screens, rarely used utilities, edge-case workflows)
- Remove the reverse proxy routing layer once 100 percent of traffic goes to the new frontend
- Decommission the JSF application server
- Remove JSF dependencies from the Java build
- Document the new architecture for ongoing maintenance
This timeline assumes the strangler fig approach with a dedicated migration team. For a detailed framework on managing legacy system transitions end-to-end, see our legacy systems migration playbook.
Team Upskilling: Turning Java Developers into Full-Stack Engineers
The migration is not just a technical project. It is a team transformation. Your JSF developers need to become proficient in modern frontend development, and this does not happen through a week-long training course. It happens through structured, hands-on practice during the migration itself.
A practical upskilling plan
- Pair experienced frontend engineers with your Java team. If you do not have in-house frontend expertise, bring in full-cycle development engineers who can lead by example. Every code review, every architecture discussion, every debugging session is a learning opportunity.
- Start with TypeScript. Java developers already understand static typing, object-oriented patterns, and interfaces. TypeScript (currently at version 5.3) is the shortest path from Java to productive frontend development. Its decorator support, generics, and interface system will feel familiar. Skip plain JavaScript entirely.
- Assign migration pages by increasing complexity. Start each developer with a simple, read-only page (a list view, a dashboard). Progress to form-heavy pages. Then complex interactive workflows. Complexity should increase gradually as confidence grows.
- Invest in frontend testing skills. Java developers understand unit testing, but frontend testing (component testing with Testing Library, E2E testing with Cypress or Playwright) has different patterns and philosophies. Make testing a core part of the upskilling plan, not an afterthought.
- Create internal documentation. As the team learns, have them document the patterns, conventions, and decisions specific to your application. This documentation becomes the onboarding resource for future hires.
The skill gap reality
Be honest about the skill gap. Most experienced JSF developers have limited exposure to JavaScript module systems, component-based architectures, CSS-in-JS or utility-first CSS, client-side routing, and state management libraries. Expecting them to be productive in week one is unrealistic. Budget 4 to 8 weeks for initial productivity and 3 to 6 months for true proficiency. Having a core group of experienced frontend engineers on the team during this transition dramatically accelerates the learning curve.
Common Pitfalls and How to Avoid Them
Pitfall 1: Trying to replicate JSF behavior exactly
JSF has specific UI patterns (partial page rendering with AJAX, server-side view state, conversation scopes) that do not have direct equivalents in modern frameworks. Do not try to recreate them. Instead, implement the same user-facing functionality using the modern framework's native patterns. The goal is feature parity from the user's perspective, not technical parity from the framework's perspective.
Pitfall 2: Migrating and re-engineering simultaneously
Resist the temptation to "improve" the business logic while migrating the UI. A migration project has enough risk without adding feature changes and architectural redesigns to the same scope. Migrate first, then iterate. If stakeholders request new features during migration, build them in the modern framework but do not change the underlying business rules in the same sprint.
Pitfall 3: Ignoring session state complexity
JSF's conversation and view scopes store significant state on the server. Modern frontends manage state on the client. Mapping JSF's server-side state to a client-side state management solution (Redux, NgRx, Pinia) requires careful analysis of each page's state requirements. Skipping this analysis leads to bugs where data is lost between navigations or forms lose their values unexpectedly.
Pitfall 4: Underestimating the dual-stack maintenance burden
During the migration period, you are maintaining two frontend applications. Every shared change (authentication updates, branding changes, new navigation items) needs to be implemented in both systems. Plan for this overhead in your sprint capacity. The faster you migrate, the less time you spend maintaining two systems.
Pitfall 5: Lack of automated testing for parity
How do you know the migrated page behaves the same as the original? Without automated E2E tests that verify functional parity, you rely on manual testing, which does not scale. Write Cypress or Playwright tests against the JSF pages before migration, then run the same tests against the new pages after. This is the only reliable way to catch regressions.
When to Ask for Help
JSF migration is a well-understood problem with proven solutions, but it requires specific expertise that many teams do not have in-house. If your team lacks experience with modern frontend architectures, API layer design, or incremental migration patterns, bringing in engineers who have completed this transition before saves months of trial and error.
DSi has Java engineers with deep experience in legacy modernization, including JSF-to-modern-frontend migrations. Whether you need architects to design the migration strategy, full-stack engineers to execute the migration alongside your team, or frontend specialists to accelerate the upskilling of your Java developers, our full-cycle development teams integrate directly into your workflow.
Conclusion
Migrating from JSF to a modern frontend framework is not optional for Java applications that need to remain competitive. The good news is that it does not have to be a high-risk, all-or-nothing rewrite. The strangler fig pattern, a clean API layer, preserved business logic, and structured team upskilling make it possible to migrate incrementally while continuing to deliver value to users every sprint.
Start by choosing your target framework based on your team's strengths and hiring plans. Set up the reverse proxy and authentication bridge. Migrate one page as a proof of concept. Then build velocity, page by page, until the JSF application can be retired. The teams that execute this well do not just end up with a modern frontend. They end up with a better architecture, a more capable engineering team, and a product that can evolve at the speed the business demands.
The worst time to start a JSF migration was two years ago. The second worst time is next quarter. Start now, start small, and let the momentum build.