30 KiB
Converting API Explorer II to Svelte
Document Version: 1.0
Date: December 2024
Author: Development Team
Status: Exploration / Proposal
Executive Summary
This document explores the feasibility, benefits, challenges, and strategy for converting API Explorer II from Vue 3 to Svelte, aligning with the OBP-Portal architecture. The analysis covers technical considerations, migration strategies, effort estimation, and recommendations.
Key Findings:
- Svelte offers significant performance and bundle size improvements
- Migration is feasible but requires substantial effort (estimated 4-8 weeks)
- Alignment with OBP-Portal would improve maintainability across projects
- Backend (Express + TypeScript) remains unchanged
Table of Contents
- Current Architecture
- Why Consider Svelte?
- Svelte vs Vue 3 Comparison
- OBP-Portal Architecture Reference
- Migration Strategy
- Technical Considerations
- Effort Estimation
- Risks and Challenges
- Benefits Analysis
- Recommendations
- Appendix
Current Architecture
Frontend Stack (Vue 3)
API Explorer II (Current)
├── Vue 3.4+ (Composition API)
├── TypeScript 5+
├── Vite 5+ (Build tool)
├── Element Plus (UI Components)
├── Vue Router
├── Pinia (State Management)
├── Highlight.js (Code highlighting)
└── i18n (Internationalization)
Target Architecture (Svelte 5)
├── Svelte 5+ (with Runes)
├── SvelteKit 2+
├── TypeScript 5+
├── Vite 5+ (Build tool)
├── Component Library (TBD)
├── Built-in routing
├── Svelte Stores (State Management)
└── svelte-i18n
Backend Stack (Unchanged)
Express + TypeScript
├── routing-controllers
├── TypeDI (Dependency Injection)
├── Express Session
├── OAuth2 (Arctic library)
├── Redis (Session storage)
└── Custom OBP API Client
Current File Structure
src/
├── components/ # Vue SFCs
│ ├── Content.vue
│ ├── Menu.vue
│ ├── Preview.vue
│ └── SearchNav.vue
├── obp/ # API logic
│ ├── index.ts
│ ├── resource-docs.ts
│ ├── message-docs.ts
│ └── api-version.ts
├── views/ # Route views
├── router/ # Vue Router config
├── stores/ # Pinia stores
└── main.ts # App entry
Why Consider Svelte?
1. Alignment with OBP-Portal
The OBP-Portal project already uses Svelte, creating an opportunity for:
- Shared knowledge: Developers can work across both projects seamlessly
- Reusable components: Common UI components can be shared
- Consistent patterns: Unified architecture across OBP ecosystem
- Reduced cognitive load: One framework to master instead of two
2. Performance Benefits
Svelte compiles to vanilla JavaScript, resulting in:
- Smaller bundle sizes: No runtime framework overhead
- Faster execution: No virtual DOM diffing
- Better TTI (Time to Interactive): Faster initial load
- Reduced memory footprint: Less JavaScript to parse and execute
3. Developer Experience
Svelte 5 with Runes offers:
- Less boilerplate: No
ref(),reactive(), orcomputed() - Runes-based reactivity: Modern, explicit reactivity with
$state,$derived,$effect - Better TypeScript support: Improved type inference and autocomplete
- Cleaner syntax: Less ceremony, more readable code
- Built-in animations: No additional libraries needed
- Scoped styles: CSS scoped by default without configuration
- Fine-grained reactivity: More performant than Svelte 4
4. Modern Tooling
- SvelteKit 2.x: Full-stack framework with SSR/SSG capabilities
- Svelte 5: Latest version with Runes for better reactivity
- Vite native support: Already using Vite, smooth transition
- TypeScript support: First-class TypeScript integration with improved inference
- Testing: Vitest works seamlessly with Svelte 5
Svelte vs Vue 3 Comparison
Code Comparison
Vue 3 Component (Current)
<template>
<div class="api-endpoint">
<h3>{{ title }}</h3>
<button @click="fetchData">{{ loading ? 'Loading...' : 'Fetch' }}</button>
<div v-if="error" class="error">{{ error }}</div>
<ul v-else>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { get } from '../obp'
const props = defineProps<{ endpoint: string }>()
const emit = defineEmits<{ success: [data: any] }>()
const items = ref<any[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
const title = computed(() => props.endpoint.toUpperCase())
async function fetchData() {
loading.value = true
error.value = null
try {
const response = await get(props.endpoint)
items.value = response.data
emit('success', response)
} catch (err: any) {
error.value = err.message
} finally {
loading.value = false
}
}
onMounted(() => {
fetchData()
})
</script>
<style scoped>
.api-endpoint {
padding: 1rem;
}
.error {
color: red;
}
</style>
Svelte 5 Component (with Runes)
<script lang="ts">
import { get } from '../obp'
interface Props {
endpoint: string
}
let { endpoint }: Props = $props()
// Runes-based reactive state
let items = $state<any[]>([])
let loading = $state(false)
let error = $state<string | null>(null)
// Derived state - automatically computed
let title = $derived(endpoint.toUpperCase())
async function fetchData() {
loading = true
error = null
try {
const response = await get(endpoint)
items = response.data
onsuccess?.(response)
} catch (err: any) {
error = err.message
} finally {
loading = false
}
}
// Effect runs on mount
$effect(() => {
fetchData()
})
// Event handler prop
let { onsuccess }: { onsuccess?: (data: any) => void } = $props()
</script>
<div class="api-endpoint">
<h3>{title}</h3>
<button onclick={fetchData}>
{loading ? 'Loading...' : 'Fetch'}
</button>
{#if error}
<div class="error">{error}</div>
{:else}
<ul>
{#each items as item (item.id)}
<li>{item.name}</li>
{/each}
</ul>
{/if}
</div>
<style>
.api-endpoint { padding: 1rem; }
.error { color: red; }
</style>
Key Differences
| Aspect | Vue 3 | Svelte 5 (with Runes) |
|---|---|---|
| Reactivity | ref(), reactive() |
$state(), $state.raw() |
| Computed | computed() function |
$derived() rune |
| Props | defineProps<T>() |
let { prop } = $props() |
| Events | defineEmits<T>() |
Event handler props (e.g., onclick) |
| Lifecycle | onMounted(), onUnmounted() |
$effect() rune |
| Conditionals | v-if, v-else |
{#if}, {:else}, {/if} |
| Loops | v-for |
{#each}, {/each} |
| Event Binding | @click |
onclick (native) |
| Bundle Size | ~34 KB (runtime) | ~2 KB (compiler output) |
| Performance | Virtual DOM | Direct DOM, fine-grained reactivity |
OBP-Portal Architecture Reference
OBP-Portal Stack
OBP-Portal (Current)
├── SvelteKit 2.x
├── Svelte 4.x (legacy syntax)
├── TypeScript
├── Vite
├── Tailwind CSS
├── Svelte Stores (State)
├── Svelte Router (built-in)
└── OAuth2 integration
Recommended Update
├── SvelteKit 2.x
├── Svelte 5.x (with Runes)
├── TypeScript
├── Vite
├── Tailwind CSS
├── Svelte 5 Runes ($state, $derived)
├── Svelte Router (built-in)
└── OAuth2 integration
Lessons from OBP-Portal
What Works Well:
- SvelteKit's file-based routing - Intuitive and maintainable
- Svelte stores - Simple, effective state management
- TypeScript integration - Smooth developer experience
- Build performance - Fast development and production builds
- Component reusability - Easy to create shared components
Challenges Encountered:
- SSR complexity - Server-side rendering requires careful handling
- Third-party libraries - Some Vue/React libraries need Svelte alternatives
- Learning curve - Team needs time to adapt to Svelte paradigms (especially Runes)
- Ecosystem - Smaller ecosystem compared to Vue/React
- Migration to Runes - OBP-Portal itself may need updating from Svelte 4 to Svelte 5
Reusable Patterns for Svelte 5
// Shared authentication state with Runes
// lib/stores/auth.svelte.ts
export class AuthStore {
user = $state<User | null>(null)
isAuthenticated = $derived(this.user !== null)
setUser(newUser: User | null) {
this.user = newUser
}
}
export const authStore = new AuthStore()
// Shared API client pattern (unchanged)
// lib/api.ts
export async function obpGet(path: string) {
const response = await fetch(`/api/get?path=${encodeURIComponent(path)}`)
return response.json()
}
// Shared component pattern with Runes
// components/OBPButton.svelte
<script lang="ts">
interface Props {
variant?: 'primary' | 'secondary'
disabled?: boolean
onclick?: () => void
}
let {
variant = 'primary',
disabled = false,
onclick
}: Props = $props()
</script>
<button class="obp-btn {variant}" {disabled} {onclick}>
{@render children?.()}
</button>
Migration Strategy
Approach: Incremental Migration
Phase 1: Preparation (1 week)
- Set up parallel Svelte build configuration
- Create proof-of-concept with 1-2 simple components
- Establish component migration patterns
- Set up testing infrastructure for Svelte
- Document migration guidelines
Phase 2: Core Infrastructure (2 weeks)
- Migrate routing (Vue Router → SvelteKit routing)
- Migrate state management (Pinia → Svelte stores)
- Migrate API client layer (minimal changes)
- Set up i18n for Svelte
- Create Svelte equivalents of utility functions
Phase 3: Component Migration (3-4 weeks)
- Migrate simple components first (buttons, cards, etc.)
- Migrate medium complexity components (forms, tables)
- Migrate complex components (SearchNav, Content, Preview)
- Replace Element Plus with Svelte alternatives
- Option A: Carbon Components Svelte
- Option B: Svelte Material UI
- Option C: Build custom components
Phase 4: Integration & Testing (1-2 weeks)
- End-to-end testing
- Performance benchmarking
- Accessibility audit
- Browser compatibility testing
- Documentation updates
Phase 5: Deployment
- Staged rollout
- Monitor for issues
- Gather user feedback
- Performance monitoring
Alternative Approach: Big Bang Rewrite
Pros:
- Clean slate, no legacy code
- Faster if done right
- No Vue/Svelte coexistence issues
Cons:
- Higher risk
- Longer time before visible progress
- More difficult to test incrementally
- Team blocked from feature development
Recommendation: Incremental migration is safer and allows parallel development.
Technical Considerations
1. UI Component Library Replacement
Current: Element Plus (Vue)
Options:
| Library | Pros | Cons |
|---|---|---|
| Carbon Components Svelte | Enterprise-grade, accessible | Large bundle, IBM-specific design |
| Svelte Material UI | Material Design, comprehensive | Material design may not fit brand |
| Attraction | Lightweight, modern | Less mature, smaller community |
| Custom Components | Full control, aligned with brand | Most effort, maintenance burden |
Recommendation: Start with Svelte Material UI or Carbon Components, create custom components where needed.
2. State Management Migration
Current: Pinia
Target: Svelte Stores
// Before (Pinia)
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({ user: null, authenticated: false }),
actions: {
setUser(user) {
this.user = user
}
}
})
// After (Svelte 5 with Runes - Recommended)
// lib/stores/user.svelte.ts
export class UserStore {
user = $state<User | null>(null)
authenticated = $derived(this.user !== null)
setUser(user: User | null) {
this.user = user
}
reset() {
this.user = null
}
}
export const userStore = new UserStore()
// Usage in component:
// import { userStore } from '$lib/stores/user.svelte'
// <p>{userStore.user?.name}</p>
// Alternative (Svelte 4 Store - Legacy)
import { writable } from 'svelte/store'
function createUserStore() {
const { subscribe, set, update } = writable({ user: null, authenticated: false })
return {
subscribe,
setUser: (user) => update((state) => ({ ...state, user })),
reset: () => set({ user: null, authenticated: false })
}
}
export const userStore = createUserStore()
3. Routing Migration
Current: Vue Router
Target: SvelteKit file-based routing
# Before (Vue Router)
src/router/index.ts → defines routes manually
# After (SvelteKit)
src/routes/
├── +page.svelte # /
├── +layout.svelte # Root layout
├── api/
│ └── [operation]/
│ └── +page.svelte # /api/:operation
└── glossary/
└── +page.svelte # /glossary
4. API Client Layer
Good News: Minimal changes required!
// src/obp/index.ts - mostly unchanged
export async function get(path: string): Promise<any> {
const response = await fetch(`/api/get?path=${encodeURIComponent(path)}`)
if (!response.ok) throw new Error('Request failed')
return response.json()
}
// Usage in Svelte 5 component with Runes
<script lang="ts">
import { get } from '$lib/obp'
let data = $state<any>(null)
let loading = $state(false)
async function loadData() {
loading = true
try {
data = await get('/obp/v5.1.0/banks')
} finally {
loading = false
}
}
// Auto-load on mount
$effect(() => {
loadData()
})
</script>
{#if loading}
<p>Loading...</p>
{:else if data}
<pre>{JSON.stringify(data, null, 2)}</pre>
{/if}
5. Backend Compatibility
Zero changes required! The Express backend remains identical:
✅ OAuth2 authentication - unchanged
✅ Session management - unchanged
✅ API proxy endpoints - unchanged
✅ Request controllers - unchanged
✅ TypeDI services - unchanged
6. Internationalization (i18n)
Current: vue-i18n
Target: svelte-i18n
// Before (vue-i18n)
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
// After (svelte-i18n with Svelte 5)
<script lang="ts">
import { _, locale } from 'svelte-i18n'
</script>
<p>{$_('welcome.message')}</p>
<button onclick={() => $locale = 'es'}>Español</button>
7. Code Highlighting
Current: Highlight.js with Vue plugin
Target: Highlight.js with Svelte action
<script lang="ts">
import hljs from 'highlight.js'
interface Props {
code: string
}
let { code }: Props = $props()
function highlight(node: HTMLElement) {
hljs.highlightElement(node)
return {
destroy() { /* cleanup if needed */ }
}
}
</script>
<pre><code use:highlight class="language-json">{code}</code></pre>
8. Build Configuration
Current: Vite + Vue plugin
Target: Vite + Svelte plugin
// vite.config.js
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
// Most config remains the same!
server: {
proxy: {
'/api': 'http://localhost:3000'
}
}
})
Effort Estimation
Time Estimates (Conservative)
| Phase | Duration | Team Size | Total Effort |
|---|---|---|---|
| Preparation | 1 week | 1 dev | 1 week |
| Core Infrastructure | 2 weeks | 2 devs | 4 weeks |
| Component Migration | 4 weeks | 2-3 devs | 8-12 weeks |
| Testing & Integration | 2 weeks | 2 devs | 4 weeks |
| Total | 9 weeks | 2-3 devs | 17-21 weeks |
Complexity Breakdown
Low Complexity (1-2 days each):
- Simple display components
- Buttons, cards, badges
- Layout components
Medium Complexity (3-5 days each):
- Form components
- Tables with sorting/filtering
- Modal dialogs
- Navigation components
High Complexity (1-2 weeks each):
- SearchNav component (API version selector, filters)
- Content component (API endpoint details, dynamic forms)
- Preview component (Try it out functionality, response display)
- Resource docs caching system
Risk Buffer
Add 25% contingency for:
- Unexpected technical challenges
- Learning curve
- Testing and bug fixes
- Documentation
Adjusted Total: 12-14 weeks (3-3.5 months)
Risks and Challenges
Technical Risks
-
Third-Party Library Compatibility
- Risk: Some libraries may not have Svelte equivalents
- Mitigation: Research alternatives early, build custom if needed
- Impact: Medium
-
Element Plus Replacement
- Risk: Feature parity with Element Plus components
- Mitigation: Incremental replacement, custom components for gaps
- Impact: High
-
SSR/Hydration Issues
- Risk: SvelteKit SSR may cause issues with OAuth flow
- Mitigation: Use client-side only mode initially, add SSR later
- Impact: Low
-
Performance Regressions
- Risk: Improper migration could reduce performance
- Mitigation: Continuous benchmarking, performance testing
- Impact: Low
Organizational Risks
-
Team Learning Curve
- Risk: Developers unfamiliar with Svelte
- Mitigation: Training sessions, pair programming, good documentation
- Impact: Medium
-
Feature Development Freeze
- Risk: No new features during migration
- Mitigation: Incremental migration allows parallel development
- Impact: Medium
-
User-Facing Bugs
- Risk: Migration introduces regressions
- Mitigation: Comprehensive testing, staged rollout, quick rollback plan
- Impact: High
-
Maintenance Burden
- Risk: Supporting two frameworks during migration
- Mitigation: Clear migration plan, time-boxed phases
- Impact: Medium
Mitigation Strategies
## Risk Mitigation Plan
### Before Migration
- [ ] Complete prototype with 3-5 key components
- [ ] Performance benchmark current Vue version
- [ ] Document all critical user flows
- [ ] Create comprehensive test suite
- [ ] Establish rollback procedures
### During Migration
- [ ] Daily progress tracking
- [ ] Weekly performance testing
- [ ] Continuous integration testing
- [ ] Stakeholder communication
- [ ] Feature freeze communication
### After Migration
- [ ] Monitor error rates
- [ ] Track performance metrics
- [ ] Gather user feedback
- [ ] Quick patch releases for bugs
- [ ] Post-mortem analysis
Benefits Analysis
Quantitative Benefits
| Metric | Vue 3 (Current) | Svelte (Expected) | Improvement |
|---|---|---|---|
| Bundle Size | ~450 KB | ~200 KB | 55% smaller |
| Initial Load | 1.2s | 0.7s | 42% faster |
| TTI | 2.5s | 1.5s | 40% faster |
| Memory Usage | 45 MB | 28 MB | 38% less |
| Build Time | 8s | 5s | 37% faster |
Note: These are estimated improvements based on typical Vue-to-Svelte migrations.
Qualitative Benefits
For Developers:
- ✅ Simpler, more readable code
- ✅ Less boilerplate
- ✅ Faster development iteration
- ✅ Unified stack with OBP-Portal
- ✅ Easier onboarding for new developers
For Users:
- ✅ Faster page loads
- ✅ Smoother interactions
- ✅ Better mobile performance
- ✅ Reduced data usage
- ✅ More responsive UI
For Organization:
- ✅ Consistent technology stack
- ✅ Shared components across projects
- ✅ Easier knowledge transfer
- ✅ Reduced maintenance overhead
- ✅ Modern, future-proof architecture
Cost-Benefit Analysis
Costs:
- 3-4 months of development effort
- Learning curve for team
- Temporary feature freeze
- Testing and QA effort
Benefits:
- Long-term maintainability
- Performance improvements
- Stack alignment
- Developer productivity gains
- User experience improvements
Break-Even Point: Estimated 6-9 months after migration completion
Recommendations
Option 1: Full Migration (Recommended for Long-Term)
When:
- Planning major feature updates
- Have 3-4 months available
- Want full alignment with OBP-Portal
Pros:
- Complete modernization
- Maximum long-term benefits
- Clean, maintainable codebase
Cons:
- Significant upfront effort
- Temporary feature freeze
- Higher risk
Option 2: Hybrid Approach
When:
- Need to maintain feature velocity
- Limited developer resources
- Risk-averse environment
Strategy:
- Keep Vue for existing features
- Build new features in Svelte
- Gradual replacement over time
Pros:
- Lower risk
- Continuous feature development
- Flexible timeline
Cons:
- Maintenance complexity
- Longer transition period
- Two frameworks to support
Option 3: Stay with Vue 3
When:
- No strong business case for change
- Limited resources
- Vue 3 performance is sufficient
Pros:
- No migration effort
- Stable, known technology
- Focus on features
Cons:
- Stack fragmentation with OBP-Portal
- Miss performance benefits
- No shared components
Final Recommendation
Proceed with Option 1 (Full Migration) if:
- ✅ OBP-Portal is successful and stable with Svelte
- ✅ Team has bandwidth for 3-4 month project
- ✅ Performance improvements are valuable
- ✅ Stack alignment is a priority
Timeline Recommendation:
- Q1 2025: Planning and preparation
- Q2 2025: Core infrastructure and component migration
- Q3 2025: Testing, deployment, stabilization
Appendix
A. Component Inventory
Current Vue components requiring migration:
src/components/
├── Content.vue [HIGH COMPLEXITY] - 500+ lines, dynamic forms
├── Menu.vue [MEDIUM] - Navigation, state management
├── Preview.vue [HIGH COMPLEXITY] - API testing, response handling
├── SearchNav.vue [HIGH COMPLEXITY] - Search, filters, collections
├── Footer.vue [LOW] - Simple layout
├── Header.vue [MEDIUM] - Auth status, user menu
└── ... (15 more components)
B. Svelte Learning Resources
Official Documentation:
- Svelte Tutorial: https://svelte.dev/tutorial
- SvelteKit Docs: https://kit.svelte.dev/docs
- Svelte Society: https://sveltesociety.dev/
Migration Guides:
- Vue to Svelte: https://svelte.dev/blog/frameworks-without-the-framework
- Component Patterns: https://svelte.dev/repl
Video Tutorials:
- Svelte Crash Course (YouTube)
- SvelteKit Full Tutorial
- Svelte State Management
C. Component Library Comparison
| Feature | Element Plus | Carbon Svelte | Svelte Material UI |
|---|---|---|---|
| Buttons | ✅ | ✅ | ✅ |
| Forms | ✅ | ✅ | ✅ |
| Tables | ✅ | ✅ | ✅ |
| Modals | ✅ | ✅ | ✅ |
| Notifications | ✅ | ✅ | ✅ |
| Date Pickers | ✅ | ✅ | ✅ |
| Trees | ✅ | ✅ | ❌ |
| Bundle Size | Large | Large | Medium |
| TypeScript | ✅ | ✅ | ✅ |
| Accessibility | Good | Excellent | Good |
D. Performance Benchmarks
# Test setup
npm run build
npm run lighthouse
# Results (estimated)
Vue 3 (Current):
- First Contentful Paint: 1.2s
- Largest Contentful Paint: 2.5s
- Total Blocking Time: 180ms
- Cumulative Layout Shift: 0.05
- Speed Index: 2.3s
Svelte (Expected):
- First Contentful Paint: 0.7s
- Largest Contentful Paint: 1.5s
- Total Blocking Time: 80ms
- Cumulative Layout Shift: 0.02
- Speed Index: 1.4s
E. Migration Checklist Template
## Component Migration Checklist
### Pre-Migration
- [ ] Document component API (props, events, slots)
- [ ] Identify dependencies
- [ ] Write unit tests (if missing)
- [ ] Screenshot current behavior
- [ ] Note any quirks or edge cases
- [ ] Check if component uses reactive patterns suitable for Runes
### Migration
- [ ] Create new Svelte 5 component file
- [ ] Convert template syntax
- [ ] Convert script logic to use Runes ($state, $derived, $effect)
- [ ] Convert props using $props()
- [ ] Convert events to event handler props
- [ ] Convert styles
- [ ] Update imports/exports
- [ ] Test in isolation
### Post-Migration
- [ ] Verify visual appearance matches
- [ ] Test all interactions
- [ ] Verify reactivity works correctly with Runes
- [ ] Check accessibility
- [ ] Performance test (should be better with Runes)
- [ ] Update documentation
- [ ] Code review
### Integration
- [ ] Update parent component imports
- [ ] Test in full application
- [ ] Cross-browser testing
- [ ] Deploy to staging
- [ ] User acceptance testing
F. Code Conversion Patterns
Pattern 1: Reactive State
// Vue 3
const count = ref(0)
const doubled = computed(() => count.value * 2)
// Svelte 5 (Runes - Recommended)
let count = $state(0)
let doubled = $derived(count * 2)
// Svelte 4 (Legacy)
let count = 0
$: doubled = count * 2
Pattern 2: Props and Events
// Vue 3
const props = defineProps<{ title: string }>()
const emit = defineEmits<{ close: [] }>()
// Svelte 5 (Runes - Recommended)
interface Props {
title: string
onclose?: () => void
}
let { title, onclose }: Props = $props()
// Call event: onclose?.()
// Svelte 4 (Legacy)
export let title: string
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
dispatch('close')
Pattern 3: Lifecycle
// Vue 3
onMounted(() => console.log('mounted'))
onUnmounted(() => console.log('cleanup'))
// Svelte 5 (Runes - Recommended)
$effect(() => {
console.log('effect runs on mount and when dependencies change')
return () => console.log('cleanup')
})
// Svelte 4 (Legacy)
import { onMount, onDestroy } from 'svelte'
onMount(() => console.log('mounted'))
onDestroy(() => console.log('cleanup'))
Pattern 4: Conditional Rendering
<!-- Vue 3 -->
<div v-if="condition">Show</div>
<div v-else>Hide</div>
<!-- Svelte -->
{#if condition}
<div>Show</div>
{:else}
<div>Hide</div>
{/if}
Pattern 5: List Rendering
<!-- Vue 3 -->
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
<!-- Svelte 5 (same as Svelte 4) -->
<ul>
{#each items as item (item.id)}
<li>{item.name}</li>
{/each}
</ul>
<!-- Note: With Runes, items would be: let items = $state([...]) -->
Conclusion
Converting API Explorer II from Vue 3 to Svelte 5 with Runes is technically feasible and offers significant long-term benefits, particularly in alignment with OBP-Portal (which should also upgrade to Svelte 5) and performance improvements. The migration requires 3-4 months of focused effort but positions the project for better maintainability, developer experience, and future-proof architecture.
Why Svelte 5 with Runes?
- Modern reactivity: Fine-grained, explicit reactivity system
- Better performance: More efficient than both Vue 3 and Svelte 4
- Improved TypeScript: Better type inference and autocomplete
- Future-proof: Official direction for Svelte going forward
- Cleaner code: Less magic, more explicit and understandable
Next Steps:
- Review this document with the team
- Build a proof-of-concept with 2-3 key components using Svelte 5 Runes
- Evaluate if OBP-Portal should also upgrade to Svelte 5
- Make a go/no-go decision based on PoC results
- If approved, create detailed migration plan
- Begin Phase 1 preparation
Important Note on OBP-Portal:
If OBP-Portal is currently using Svelte 4, consider upgrading it to Svelte 5 first or in parallel. This ensures both projects use the same modern patterns and maximizes code sharing opportunities.
Questions or Feedback?
Please contact the development team or open a discussion in the project repository.
Document History:
- v1.0 - December 2024 - Initial exploration document
- v1.1 - December 2024 - Updated to focus on Svelte 5 with Runes