Reactive streams built on async iterators. Small bundle, pull-based execution, and a familiar operator API.
Developer toolsTypeScript · JavaScriptGitHub @epikodelabsConnection unstableOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Reactive runtime built on async iterators
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Configure your editor and build tools, then create your first reactive counter in Angular, React, or Vue. Start with an existing application and the Node.js and TypeScript versions supported by your framework.
| Framework | Requirements | Source preprocessing |
|---|---|---|
| Angular | Angular 20+, TypeScript 5.5+ compatible with your Angular version | Angular builder |
| React | React 18+, TypeScript 5.5+ | Vite plugin: sx() before react() |
| Vue | Vue 3.4+, TypeScript 5.5+ | Vite plugin: sxVue() before vue() |
Install @epikodelabs/streamix once; no separate compiler, builder, or editor packages are needed. Configure both the build and editor before using the examples below.
npm install @epikodelabs/streamix
npm install -D @angular/language-service
Keep the language-service version aligned with your Angular packages.
In angular.json, keep your existing Angular build target as application, then add a Streamix build wrapper. The following is an Architect target fragment, not a replacement for the whole workspace configuration. Replace my-app and the paths with your project's names.
{
"application": {
"builder": "@angular/build:application",
"options": {
"browser": "projects/my-app/src/main.ts",
"tsConfig": ".angular/.streamix/my-app/tsconfig.app.json",
"fileReplacements": [
{
"replace": "projects/my-app/src/main.ts",
"with": ".angular/.streamix/my-app/src/main.ts"
}
]
}
},
"build": {
"builder": "@epikodelabs/streamix/angular/builder:application",
"options": {
"delegateTarget": "my-app:application",
"sourceRoot": "projects/my-app/src"
}
},
"serve": {
"builder": "@epikodelabs/streamix/angular/builder:application",
"options": {
"delegateTarget": "my-app:dev-server",
"sourceRoot": "projects/my-app/src"
}
},
"dev-server": {
"builder": "@angular/build:dev-server",
"options": {
"buildTarget": "my-app:application"
}
}
}
Merge the application.options shown above into your existing Angular options, preserving index, assets, styles, output paths, and configurations. Move the original serve target to dev-server and make it delegate to application, so preprocessing runs once. For named development/production configurations, preserve those names on the wrappers and their delegates.
Keep editing your source files. Use .angular/.streamix/my-app/ only as generated build output, and point the delegate at its tsconfig and entry point.
Merge this into the application's TypeScript configuration:
{
"compilerOptions": {
"plugins": [
{
"name": "@epikodelabs/streamix/angular/editor/tsserver"
}
]
}
}
Restart the TypeScript server. Use an inline template for the atom shorthand below. In external templateUrl files, use explicit .value reads for editor checking; scope fields are typed as values.
Use this standalone component in your application's component tree:
import { Component } from '@angular/core';
import { scope } from '@epikodelabs/streamix';
@Component({
selector: 'app-counter',
standalone: true,
template: '<button (click)="state.count += 1">{{ state.count }}</button>',
})
export class CounterComponent {
readonly state = scope({ count: 0 });
}
Scope fields support direct reads and writes in templates.
For Zone.js applications, add provideSxZoneScheduling() from @epikodelabs/streamix/angular to your existing application providers. Zoneless applications need no provider.
npx ng serve my-app
npx ng build my-app
Clicking the button should increment the displayed value. Continue with Angular for scopes, two-way bindings, compiler boundaries, and SSR.
In an existing React/Vite application:
npm install @epikodelabs/streamix
npm install -D typescript vite @vitejs/plugin-react
Install TypeScript even in JavaScript-only projects.
Preprocess sources with sx() before the React plugin in vite.config.ts. Use this configuration for both the development server and production builds. Keep your other application settings.
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { sx } from '@epikodelabs/streamix/react/vite';
export default defineConfig({
plugins: [sx(), react()],
});
Add this to the tsconfig that includes your React source files, such as tsconfig.app.json:
{
"compilerOptions": {
"plugins": [
{
"name": "@epikodelabs/streamix/react/editor/tsserver"
}
]
}
}
Restart the TypeScript server after saving the configuration.
Render this component from your existing application root:
import { atom } from '@epikodelabs/streamix';
export function Counter() {
let count = atom(0);
return <button onClick={() => count++}>{count}</button>;
}
Declare local reactive state directly in the component body, before conditional returns.
npx vite
npx vite build
Clicking the button should increment the displayed value. Continue with React for scopes, component props, explicit runtime APIs, and SSR.
In an existing Vue/Vite application:
npm install @epikodelabs/streamix
npm install -D typescript vite @vitejs/plugin-vue
Preprocess Vue SFCs with sxVue() before Vue's SFC plugin in vite.config.ts. Use this configuration for both the development server and production builds:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { sxVue } from '@epikodelabs/streamix/vue/vite';
export default defineConfig({
plugins: [sxVue(), vue()],
});
Add the Vue Language Tools plugin to the application's tsconfig, or the config extended by tsconfig.app.json:
{
"vueCompilerOptions": {
"plugins": [
"@epikodelabs/streamix/vue/editor/tsserver"
]
}
}
Register this under vueCompilerOptions.plugins, not compilerOptions.plugins. Restart the Vue language server; in VS Code, reload the window if diagnostics do not refresh.
Mount this component from your existing application:
<script setup lang="ts">
import { atom } from '@epikodelabs/streamix';
const count = atom(0);
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
Declare the atom in <script setup> and use count directly in the template, as shown above.
npx vite
npx vite build
Clicking the button should increment the displayed value. Continue with Vue for scopes, v-model, explicit bridges, and SSR.
Set introspection to true to inspect reactive state while developing. Use the browser panel to check current values, dependencies, subscriptions, flow activity, and cleanup. Source names, file locations, and scope ownership help you find the corresponding code.
The option is a boolean and defaults to false. Configure it on the Angular builder or the React/Vue Vite plugin.
In angular.json, merge these configurations into both the build and serve wrapper targets configured above. Keep their existing builder, delegateTarget, sourceRoot, and other configuration settings.
{
"configurations": {
"development": { "introspection": true },
"production": { "introspection": false }
}
}
Start the application with the development configuration:
npx ng serve my-app --configuration development
For a diagnostic build, use npx ng build my-app --configuration development. Use --configuration production for a build without introspection.
Enable introspection for the development server in vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { sx } from '@epikodelabs/streamix/react/vite';
export default defineConfig(({ command }) => ({
plugins: [sx({ introspection: command === 'serve' }), react()],
}));
Use the same setting on sxVue() in vite.config.ts:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { sxVue } from '@epikodelabs/streamix/vue/vite';
export default defineConfig(({ command }) => ({
plugins: [sxVue({ introspection: command === 'serve' }), vue()],
}));
Run npx vite, open the application, and expand the inspector in the bottom-right corner. With these Vite configurations, npx vite build leaves introspection disabled.
With a standard entry file, the panel mounts automatically; no additional component is required. Angular uses main.ts directly under sourceRoot. React recognizes main or index with a .ts, .tsx, .js, or .jsx extension; Vue recognizes main.ts, main.js, index.ts, or index.js. Keep the entry included if you configure a plugin's include filter.
If you use a custom entry file, add activation there explicitly:
import {
enableStreamixIntrospection,
mountStreamixInspector,
} from '@epikodelabs/streamix/introspection';
// Before bootstrapping the application:
enableStreamixIntrospection();
// After bootstrapping, when document.body exists:
mountStreamixInspector();
Keep introspection: true on the builder/plugin for source metadata and diagnostic activation with a custom entry too.
Keep the option disabled for production. Setting it to true explicitly forces import.meta.env.DEV to true, even during a production build; it is not automatically restricted to the development server.
With the Streamix builder or Vite plugin configured above, use shorthand reads inside derived:
import { atom, derived } from '@epikodelabs/streamix';
const count = atom(0);
const doubled = derived(() => count * 2);
count.set(3);
console.log(doubled.value); // 6
The compiler lowers count to a tracked value read inside the formula. Without Streamix preprocessing, use derived(self => self(count) * 2). Outside compiled expressions, use .value for an explicit current-value read.
MIT