intentx-react-mvvm
v1.0.0
Published
Feature-driven MVVM runtime for React with reactive signals, ViewModels, optional dependency injection, plugins, and runtime lifecycle orchestration.
Downloads
2,568
Maintainers
Keywords
Readme
⚛️ intentx-react-mvvm
🚀 A feature-based runtime engine for React applications with reactive signals, dependency injection, ViewModels, and plugin architecture.
Build scalable frontend systems using features, services, reactive signals, and lifecycle-aware business logic.
What is intentx-react-mvvm?
intentx-react-mvvm is a lightweight runtime layer for React applications inspired by backend frameworks like NestJS, Angular, and modular runtime systems.
Instead of putting all business logic inside components, the runtime introduces:
- 🧩 Features
- 💉 Optional Dependency Injection
- 📡 Reactive Signals
- 🧠 ViewModels
- 🔌 Plugins
- ⚛️ React Bindings
while still keeping React fully in control of rendering.
Built during random “I should probably sleep” sessions.
Why intentx-react-mvvm?
As React applications scale, architecture usually becomes difficult to maintain.
Common problems:
- Business logic mixed with UI
- Large component trees
- Global state becoming difficult to organize
- Tight coupling between services and components
- Too many unnecessary re-renders
- Hard-to-test business logic
intentx-react-mvvm solves this by introducing runtime-driven architecture with isolated features and reactive state.
Features
- ⚡ Fine-grained reactive signals
- 🧩 Feature-based architecture
- 💉 Optional dependency injection
- 🧠 ViewModel pattern
- 📡 Computed signals
- ⚛️ React hooks integration
- 🔌 Plugin system
- 🚦 Lazy feature loading
- 📦 Runtime lifecycle
- 🧱 Framework-agnostic core
- ✅ Full TypeScript support
Mental Model
App
↓
Runtime
↓
Features
↓
ViewModels / Services
↓
Reactive Signals
↓
React ComponentsInstallation
npm install intentx-react-mvvmQuick Start
1. Create Feature
// features/user.feature.ts
import { defineFeature } from 'intentx-react-mvvm'
import { UserService } from './services/user.service'
import { UserVM } from './viewmodels/user.vm'
export default defineFeature({
name: 'users',
providers: [
UserService,
UserVM,
],
onInit() {
console.log('users feature mounted')
},
onDestroy() {
console.log('users feature destroyed')
},
})2. Create Service
Services are plain TypeScript classes.
They can be used:
- manually
- with dependency injection
- with runtime container
- inside ViewModels
// services/user.service.ts
import { Injectable } from 'intentx-react-mvvm'
export interface User {
id: number
name: string
email: string
}
@Injectable()
export class UserService {
async findAll(): Promise<User[]> {
await new Promise((r) => setTimeout(r, 500))
return [
{
id: 1,
name: 'John Doe',
email: '[email protected]',
},
{
id: 2,
name: 'Jane Smith',
email: '[email protected]',
},
{
id: 3,
name: 'Alice Brown',
email: '[email protected]',
},
]
}
}3. Create ViewModel
// viewmodels/user.vm.ts
import {
computed,
Injectable,
ViewModel,
} from 'intentx-react-mvvm'
import {
type User,
UserService,
} from '../services/user.service'
@Injectable({
deps: [UserService],
})
export class UserVM extends ViewModel {
readonly users = this.signal<User[]>([])
readonly keyword = this.signal('')
readonly loading = this.signal(false)
readonly filteredUsers = computed(() => {
return this.users
.get()
.filter((x) =>
x.name
.toLowerCase()
.includes(
this.keyword
.get()
.toLowerCase(),
),
)
})
constructor(
private readonly userService: UserService,
) {
super()
}
async onInit() {
await this.load()
}
async load() {
this.loading.set(true)
try {
const users =
await this.userService.findAll()
this.users.set(users)
} finally {
this.loading.set(false)
}
}
setKeyword(keyword: string) {
this.keyword.set(keyword)
}
}Manual Composition Mode
This mode skips the runtime dependency injection system entirely.
Useful for:
- small apps
- testing
- custom factories
- manual architecture control
import { defineFeature } from 'intentx-react-mvvm'
import { UserService } from './services/user.service'
import { UserVM } from './viewmodels/user.vm'
export default defineFeature({
name: 'users',
async onInit() {
const userService =
new UserService()
const userVM =
new UserVM(userService)
await userVM.load()
console.log('feature ready')
},
})4. Use ViewModel in React
import {
useSignal,
useViewModel,
} from 'intentx-react-mvvm/react'
import { UserVM } from '../viewmodels/user.vm'
export function UserPage() {
const vm = useViewModel(UserVM)
const keyword = useSignal(vm.keyword)
const users = useSignal(vm.filteredUsers)
const loading = useSignal(vm.loading)
return (
<div style={{ padding: 40 }}>
<h1>Users</h1>
<input
value={keyword}
onChange={(e) => {
vm.setKeyword(
e.currentTarget.value,
)
}}
/>
{loading && <p>Loading...</p>}
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name}
</li>
))}
</ul>
</div>
)
}Signals
import {
signal,
computed,
effect,
} from 'intentx-react-mvvm'
const count = signal(0)
const double = computed(() => {
return count.get() * 2
})
effect(() => {
console.log(count.get())
})
count.set(1)Computed Signals
const firstName = signal('John')
const lastName = signal('Doe')
const fullName = computed(() => {
return `${firstName.get()} ${lastName.get()}`
})Effects
effect(() => {
console.log(
'count changed:',
count.get(),
)
})Service Usage
Services are plain TypeScript classes.
You can use them in multiple ways depending on your architecture needs.
1. Manual Service Usage
export class UserService {
async findAll() {
return fetch('/api/users')
.then((r) => r.json())
}
}
const service = new UserService()
const users =
await service.findAll()Good for:
- small apps
- isolated modules
- testing
- simple business logic
2. ViewModel Injection (Recommended)
@Injectable({
deps: [UserService],
})
export class UserVM extends ViewModel {
constructor(
private readonly userService: UserService,
) {
super()
}
}Benefits:
- centralized dependency management
- easier testing/mocking
- feature isolation
- cleaner architecture
3. Runtime Container Access (Advanced)
const service =
runtime.container.resolve(
UserService,
)Useful for:
- plugins
- runtime extensions
- infrastructure
- dynamic systems
Avoid using this directly inside React components.
useSignal
const count = useSignal(vm.count)useViewModel
const vm = useViewModel(UserVM)Potential future factory mode:
const vm = useViewModel(() => {
return new UserVM(
new UserService(),
)
})ViewModel Lifecycle
export class UserVM extends ViewModel {
async onInit() {
console.log('mounted')
}
async onDestroy() {
console.log('destroyed')
}
}Runtime Router
RuntimeRouter automatically collects routes from registered features.
import { BrowserRouter } from 'react-router-dom'
import {
RuntimeProvider,
RuntimeRouter,
} from 'intentx-react-mvvm/react'
export default function App() {
return (
<BrowserRouter>
<RuntimeProvider runtime={runtime}>
<RuntimeRouter runtime={runtime} />
</RuntimeProvider>
</BrowserRouter>
)
}Lazy Feature Loading
await runtime.loadFeature(() =>
import('./features/admin.feature'),
)Destroy Feature
await runtime.destroyFeature('users')After destruction:
- feature removed from runtime
- providers disposed
- lifecycle hooks executed
- routes removed
- subscriptions cleaned
Plugin Ideas
- persistencePlugin
- devtoolsPlugin
- loggerPlugin
- sentryPlugin
- analyticsPlugin
- i18nPlugin
Runtime API
| API | Purpose |
| -------------------------- | ----------------------- |
| use(plugin) | Install runtime plugin |
| registerFeature(feature) | Register feature module |
| loadFeature(loader) | Lazy load feature |
| destroyFeature(name) | Dispose feature |
| getFeatures() | Get registered features |
| getRoutes() | Get runtime routes |
Comparison
| Feature | intentx-react-mvvm | Redux Toolkit | MobX | Zustand | | -------------------- | ------------------ | -------------- | ---- | -------- | | Reactive signals | ✅ | ❌ | ✅ | ⚠️ | | Dependency injection | ✅ | ❌ | ❌ | ❌ | | ViewModels | ✅ | ❌ | ⚠️ | ❌ | | Feature architecture | ✅ | ❌ | ❌ | ❌ | | Runtime lifecycle | ✅ | ❌ | ❌ | ❌ | | Plugin system | ✅ | ❌ | ❌ | ❌ | | Lazy feature loading | ✅ | ⚠️ | ❌ | ❌ | | Computed state | ✅ | ✅ | ✅ | ⚠️ | | React hooks | ✅ | ✅ | ✅ | ✅ | | Lightweight | ✅ | ⚠️ | ❌ | ✅ |
Philosophy
You control:
- Features
- Services
- ViewModels
- UI
- Plugins
intentx-react-mvvm controls:
- Runtime lifecycle
- Signals
- Feature orchestration
- React integrationPrinciples
- Explicit over implicit
- Feature-first architecture
- Runtime-oriented execution
- Composition over inheritance
- Reactive state by default
- Framework-agnostic core
- Full TypeScript inference
When to Use
✅ Great Fit
- Large React applications
- Enterprise frontends
- Plugin-based systems
- Micro-frontends
- Feature-driven architecture
- Complex business logic
❌ Probably Overkill
- Tiny apps
- Static sites
- Simple CRUD pages
- Small prototypes
License
MIT
