In a Backbone-to-React migration, building the first component is often easier than deciding who owns the data, the view lifecycle, and the behaviour of the feature. Make those decisions before rewriting. Otherwise, you can end up with two frameworks and two sources of truth while still being unable to change a form safely.
1. Define who owns the DOM
React can run inside an existing application. A useful starting point is a dedicated container owned by the legacy view. Backbone manages whether that container exists on the page; React manages its contents. jQuery-based code should not modify elements inside the same subtree.
For a client-rendered application, this entry point can use createRoot. When the legacy view is removed, call root.unmount() as well. Removing the container from the DOM alone does not give React a chance to clean up its component tree, including effects and subscriptions.
Keep the integration contract small and explicit: mount the view, pass updated data, dispose of the view. Backbone-specific details do not need to reach every component.
2. Assign one source of truth to each piece of information
Copying a Backbone model into a new store and synchronising both directions can look convenient until the first conflict. Instead, identify the owner of each piece of information: the saved entity, a field being edited, the selection in a list, or the status of a request.
During the transition, the legacy model can remain the owner of saved data. An adapter passes a snapshot through props, while React reports user intent, such as saving or deleting. Local form state then has a clear scope and a defined commit point. This does not automatically require a new global store.
Every bridge between technologies should also have a removal condition. Without one, a temporary integration can become another permanent layer of the application.
3. Migrate a user-facing feature with a clear finish line
A good candidate can be deployed and evaluated independently: a property panel, a list filter, or a specific form. Consider how often it changes, how many dependencies it has, and the cost of failure. The smallest component is not necessarily the best first step.
Before implementation, describe the behaviour that must survive: opening an existing URL, loading data, validation, saving, and handling errors. If the UX changes too, distinguish deliberate product changes from regressions. The number of rewritten components says little about whether a feature is ready.
4. Protect behaviour at the integration boundary
Test what matters to the user. Can they complete the form using a keyboard? Is a validation error visible and understandable? Does a failed save preserve their input? Does reopening the view cause duplicate event handling?
Pay particular attention to mounting, data updates, and repeatedly closing and reopening the view. These are the points where different lifecycles meet. A user-flow test and an adapter test can be more valuable here than a test that mirrors the component structure.
5. Agree on rules the whole team can use
A short decision record should describe DOM boundaries, state ownership, the integration contract, and resource cleanup. Add where new features should be built and when extending the legacy code is acceptable. Code review can then build on an agreed decision instead of restarting the same discussion.
Choose a rollback approach as well. Returning to the previous release may be enough; other situations may justify switching between implementations. Keeping both versions has a cost, so define when or under what conditions that arrangement ends.
When is a feature actually migrated?
- Users can complete the agreed scenarios, including recovering from an error.
- DOM elements and data have clearly defined owners.
- Removing the view cleans up effects and subscriptions.
- The redundant implementation and unnecessary bridges have been removed.
- The team knows where to make the next change to the feature.
This gives you a way to assess progress through features the team can develop and maintain. It is a more useful planning measure than the percentage of files rewritten in React.
