A simple, powerful state management library for Svelte applications, inspired by GetX for Flutter.
- 🚀 Simple and intuitive API
- 🎯 Dependency injection for controllers
- 🔄 Reactive state management
- 🧩 Automatic component updates
- ♻️ Optional automatic controller disposal
- 🏷️ Tag-based controller identification
# npm
npm install get-svelte
# pnpm
pnpm add get-svelte
# yarn
yarn add get-svelteFirst, create a controller that extends GetxController:
import { GetxController } from 'get-svelte';
class CounterController extends GetxController {
count = 0;
increment() {
this.count++;
this.notifyListener(); // Notify listeners about state change
}
decrement() {
this.count--;
this.notifyListener(); // Notify listeners about state change
}
// Optional: Override onInit for initialization logic
protected onInit(): void {
console.log('CounterController initialized');
}
}Register your controller with the Get service:
import { Get } from 'get-svelte';
import { CounterController } from './CounterController';
// Register controller without tag
const counterController = Get.put(new CounterController());
// Or register with a tag for specific identification
const taggedController = Get.put(new CounterController(), { tag: 'main-counter' });Use the GetListener component to automatically react to controller state changes:
<script>
import { Get, GetListener } from 'get-svelte';
import { CounterController } from './CounterController';
// Find existing controller or throw error if not found
const counterController = Get.find(CounterController);
// Or find controller by tag
const taggedController = Get.find(CounterController, { tag: 'main-counter' });
</script>
<GetListener controller={counterController}>
{#snippet builder(controller)}
<div>
<h1>Counter: {controller.count}</h1>
<button on:click={() => controller.increment()}>Increment</button>
<button on:click={() => controller.decrement()}>Decrement</button>
</div>
{/snippet}
</GetListener>The main service class for controller management:
Registers a controller with the Get system. If a controller of the same type and tag exists, returns the existing instance.
const controller = Get.put(new MyController());
const taggedController = Get.put(new MyController(), { tag: 'unique-tag' });Get.find<T extends GetxController>(ControllerClass: new (...args: any[]) => T, params?: { tag?: string }): T
Finds and returns a registered controller. Throws an error if not found.
const controller = Get.find(MyController);
const taggedController = Get.find(MyController, { tag: 'unique-tag' });Get.isRegistered<T extends GetxController>(ControllerClass: new (...args: any[]) => T, params?: { tag?: string }): boolean
Checks if a controller is registered with the Get system.
const exists = Get.isRegistered(MyController, { tag: 'unique-tag' });Base class for all controllers:
Notifies all registered listeners about state changes. Call this method whenever your controller's state changes.
Lifecycle hook called when the controller is initialized. Override this method to perform initialization tasks.
Removes the controller from the Get system.
A Svelte component that listens to controller state changes and rebuilds its UI:
<GetListener
controller={myController}
autoDestroy={true}>
{#snippet builder(controller)}
<!-- Your UI that uses controller state -->
<div>Count: {controller.count}</div>
{/snippet}
</GetListener>controller: The controller instance to listen toautoDestroy(optional, default:true): Whether to automatically dispose the controller when the component is destroyed
When using get-svelte in SSR environments, you need to prevent cross-request controller contamination.
Add this one line to your server request handler:
// SvelteKit: src/hooks.server.ts
import { Get } from 'get-svelte';
export async function handle({ event, resolve }) {
Get.clearForSSR(); // Clear controllers from previous requests
return await resolve(event);
}// Express.js
app.use((req, res, next) => {
Get.clearForSSR();
next();
});// Custom Node.js server
function handleRequest(req, res) {
Get.clearForSSR();
// ... your request handling
}In SSR environments, the static controller registry is shared across all requests. Without clearing:
- ❌ User A's data might briefly appear to User B
- ❌ Memory leaks from accumulated controllers
- ❌ Cross-request state contamination
With Get.clearForSSR():
- ✅ Each request starts with a clean slate
- ✅ Complete isolation between users
- ✅ No memory leaks
- ✅ Controllers properly disposed
// Manual cleanup (same as clearForSSR)
Get.deleteAll();
// Check if controllers exist
const hasControllers = Get.isRegistered(MyController);// CounterController.ts
import { GetxController } from 'get-svelte';
export class CounterController extends GetxController {
count = 0;
increment() {
this.count++;
this.notifyListener();
}
decrement() {
this.count--;
this.notifyListener();
}
}<!-- Counter.svelte -->
<script>
import { Get, GetListener } from 'get-svelte';
import { CounterController } from './CounterController';
// Create and register controller if not already registered
const controller = Get.isRegistered(CounterController)
? Get.find(CounterController)
: Get.put(new CounterController());
</script>
<GetListener controller={controller}>
{#snippet builder(ctrl)}
<div>
<h2>Count: {ctrl.count}</h2>
<button on:click={() => ctrl.increment()}>+</button>
<button on:click={() => ctrl.decrement()}>-</button>
</div>
{/snippet}
</GetListener>// Create multiple counter instances
const counter1 = Get.put(new CounterController(), { tag: 'counter1' });
const counter2 = Get.put(new CounterController(), { tag: 'counter2' });
// Later, retrieve specific counters
const counter1Instance = Get.find(CounterController, { tag: 'counter1' });
const counter2Instance = Get.find(CounterController, { tag: 'counter2' });<script>
import { Get, GetListener } from 'get-svelte';
import { MyController } from './MyController';
const controller = Get.put(new MyController());
function cleanupController() {
controller.dispose();
}
</script>
<GetListener
controller={controller}
autoDestroy={false}>
{#snippet builder(ctrl)}
<!-- Your UI components here -->
<div>Data: {ctrl.data}</div>
{/snippet}
</GetListener>
<button on:click={cleanupController}>Dispose Controller</button>Contributions are welcome! Please feel free to submit a Pull Request.
ISC License
Created by Abdalmonem
