equinor / equinor/fusion-framework
Fix React Router v7 compatibility in legacy-interopt package
- Dominant language
- TypeScript
- Stars
- 10
- Forks
- 10
- Avg merge
- 19h 40m
- Merged PRs (30d)
- 150
Description
# React Router v5 to v7 Migration Guide
> ref: [chore: bump react-router-dom from 5.3.4 to 7.9.1 #3380](https://github.com/equinor/fusion-framework/pull/3380)
## TL;DR - Quick Fix
**Problem**: Build failing due to `Router` component API change in React Router v7
**Solution**: Replace `Router` with `BrowserRouter` in legacy-interopt package
**Time**: ~30 minutes + testing
```bash
# 1. Fix the breaking change
# File: packages/react/legacy-interopt/src/create-legacy-render.tsx:71
-
+
# 2. Update types
# File: packages/react/legacy-interopt/package.json
- "@types/react-router-dom": "^5.3.3"
+ "@types/react-router-dom": "^7.9.1"
# 3. Test
pnpm build
```
## Current Build Error
```typescript
// packages/react/legacy-interopt/src/create-legacy-render.tsx(71,28)
error TS2322: Type '{ children: ReactNode; key: string; history: History; }'
is not assignable to type 'IntrinsicAttributes & RouterProps'.
Property 'history' does not exist on type 'IntrinsicAttributes & RouterProps'.
```
## Root Cause Analysis
### What Broke
React Router v6+ removed the `history` prop from the `Router` component:
```tsx
// β BREAKING (React Router v5)
{children}
// β FIXED (React Router v6+)
{children}
```
### Why This Happened
- **React Router v5**: `Router` component accepted custom `history` instances
- **React Router v6+**: `Router` component no longer accepts `history` prop
- **Our Code**: Legacy-interopt still uses the old v5 pattern
### What's NOT Broken
- β
**Nested Histories**: Core architecture preserved (uses `v5Compat: true`)
- β
**Navigation Module**: Uses `@remix-run/router` (fully compatible)
- β
**Modern Apps**: Already use `RouterProvider` pattern
- β
**Type Safety**: Just need to update `@types/react-router-dom`
## Technical Details
### Architecture Overview
```
βββββββββββββββββββββββββββββββββββββββ
β Framework Layer (@remix-run/router) β β Stable, no changes needed
β - Route creation β
β - History management β
β - Navigation logic β
βββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Application Layer (react-router) β β Updated to v7
β - UI components β
β - React hooks β
β - User interactions β
βββββββββββββββββββββββββββββββββββββββ
```
### Package Status
| Package | Pattern | v7 Compatible | Action Needed |
|---------|---------|---------------|---------------|
| `dev-portal` | `RouterProvider` | β
Yes | None |
| `cookbooks/*` | `RouterProvider` | β
Yes | None |
| `legacy-interopt` | `Router` | β No | **Fix Router component** |
### Why Nested Histories Still Work
The nested history logic operates at the **Navigator level**, not the React Router level:
```typescript
// This logic is UNCHANGED and still works
useLayoutEffect(() => {
return framework.modules.navigation.navigator.listen((update) => {
// Custom nested history synchronization
const appLocation = createLocation(/* ... */);
history.push(appLocation); // Uses Navigator, not React Router
});
}, [appKey, basename, framework, history]);
```
## Step-by-Step Fix
### Step 1: Fix Router Component
**File**: `packages/react/legacy-interopt/src/create-legacy-render.tsx`
```tsx
// Line 71: Replace this
{children}
// With this
{children}
```
**Why this works**: The nested history logic is handled by the `Navigator` class, not the React Router component.
### Step 2: Update Type Definitions
**File**: `packages/react/legacy-interopt/package.json`
```json
{
"devDependencies": {
"@types/react-router-dom": "^7.9.1"
}
}
```
### Step 3: Verify v5Compat Mode
**File**: `packages/modules/navigation/src/createHistory.ts`
Ensure all history types use `v5Compat: true`:
```typescript
return createBrowserHistory({ v5Compat: true });
return createHashHistory({ v5Compat: true });
return createMemoryHistory({ v5Compat: true });
```
### Step 4: Test the Fix
```bash
# Build all packages
pnpm build
# Test specific package
pnpm --filter @equinor/fusion-framework-legacy-interopt build
# Run tests
pnpm test
```
## Testing Checklist
### Build Verification
- [ ] `pnpm build` passes without errors
- [ ] TypeScript compilation succeeds
- [ ] No new linting errors
### Legacy App Integration
- [ ] Legacy app loads without errors
- [ ] Navigation between routes works
- [ ] Context switching functions correctly
- [ ] Nested history synchronization works
### Modern App Testing
- [ ] RouterProvider usage works
- [ ] Navigation hooks function correctly
- [ ] Route rendering is correct
## Navigation Module Analysis
### Current Architecture (No Changes Needed)
- **Framework**: Uses `@remix-run/router@1.23.0` (stable, mature)
- **Applications**: Use `react-router-dom@7.9.1` (latest features)
- **Compatibility**: β
**FULLY COMPATIBLE**
### Why Keep Current Architecture
1. **Framework Agnostic**: Navigation module independent of React Router versions
2. **Stable Core**: `@remix-run/router` is the core routing engine
3. **Version Independence**: No tight coupling between framework and apps
4. **Future-Proof**: Can adapt to future React Router versions
### Route Compatibility
```typescript
// Navigation Module creates router
const router = createRouter({
basename: '/apps/my-app',
history: customHistory,
routes: AgnosticRouteObject[], // β Compatible with React Router v7
future: {
v7_prependBasename: true, // β Already v7-ready!
},
});
// Applications consume seamlessly
// β Works perfectly
```
## Deprecation Notice
> **β οΈ IMPORTANT**: `@equinor/fusion-framework-legacy-interopt` is scheduled for deprecation in **October 2024**. This React Router v7 fix is a **temporary solution** while planning the transition to modern React Router patterns for legacy applications.
### Long-term Planning (Post-October 2024)
1. **Plan Legacy App Migration**: Prepare for deprecation of legacy-interopt package
2. **Modern Router Patterns**: Migrate legacy apps to use modern React Router patterns
3. **Framework Integration**: Ensure legacy apps can integrate with modern navigation module
4. **Timeline**: Complete migration before October 2024 deprecation
## Risk Assessment
### Low Risk Factors
- β
Nested histories architecture is preserved
- β
v5Compat flag maintains v5 behavior
- β
Most packages already use modern patterns
- β
Framework abstraction provides good isolation
### Mitigation Strategies
1. **Incremental Testing**: Test each package individually
2. **Rollback Plan**: Keep v5 branch for quick rollback
3. **Legacy App Testing**: Comprehensive testing of legacy integration
4. **Type Safety**: Update all type definitions
## Common Issues & Solutions
### Issue: TypeScript errors after fix
**Solution**: Ensure `@types/react-router-dom` is updated to v7.9.1
### Issue: Legacy app navigation not working
**Solution**: Verify `v5Compat: true` is set in history creation
### Issue: Build still failing
**Solution**: Check that all `Router` components are replaced with `BrowserRouter`
## Summary
### What Needs to Change
- **1 file**: `packages/react/legacy-interopt/src/create-legacy-render.tsx` (Router β BrowserRouter)
- **1 package**: `@types/react-router-dom` (v5.3.3 β v7.9.1)
### What Stays the Same
- **Navigation Module**: No changes needed (uses `@remix-run/router`)
- **Nested Histories**: Core architecture preserved
- **Modern Apps**: Already compatible with v7
### Estimated Effort
- **Critical Fix**: 30 minutes
- **Testing**: 2-4 hours
- **Total**: Half day
### Success Criteria
- [ ] Build passes
- [ ] Legacy apps integrate correctly
- [ ] Nested histories work as expected
- [ ] Modern apps function normally
- [ ] Navigation module remains agnostic
Contributor guide
Assessment
This issue has not been assessed yet.