Migrating from Svelte
Svelte is the closest framework to AkashJS. Both use a compiler, both have signals-based reactivity, and both emphasize small bundles with minimal runtime. If you know Svelte 5 runes, the transition is nearly 1:1.
Concept Mapping
| Svelte 5 | AkashJS | Notes |
|---|---|---|
$state() | signal() | Same concept. Read with signal() instead of direct access. |
$derived() | computed() | Same concept. |
$effect() | effect() | Same concept. |
$props() | Props interface + ctx.props | TypeScript interface for type safety. |
$bindable() | Two-way binding via callback props | Pass onInput callback + value signal. |
{#if} | <Show when={...}> or :if | Same concept. |
{#each} | <For each={...}> or :for | Same concept, callback render function. |
{#await} | <Suspense> + createResource() | Different API, same result. |
transition: | <Transition> component | Component instead of directive. |
use: action | defineDirective() | Same lifecycle concept. |
Svelte stores (writable) | defineStore() | More structured (state + getters + actions). |
| SvelteKit | @akashjs/router + SSR | File-based routing, loaders, guards. |
+page.svelte | page.akash | Same convention, different file extension. |
+layout.svelte | layout.akash | Same convention. |
+page.server.ts (load) | loader.ts | Same concept, different file. |
onMount() | onMount() | Identical name and behavior. |
onDestroy() | onUnmount() | Different name, same concept. |
<slot> | ctx.children() | Function call instead of element. |
bind:value | Manual value + onInput | Explicit binding. |
Snippets ({#snippet}) | Callback props (render functions) | Pass render functions as props. |
Key Differences
Signal Reads Are Explicit
Svelte 5 runes look like plain variables. AkashJS signals are function calls.
// Svelte 5
let count = $state(0);
console.log(count); // just read the variable
count++; // just assign
// AkashJS
const count = signal(0);
console.log(count()); // call the signal
count.update(c => c + 1); // use .set() or .update()Why function calls?
Explicit signal reads make dependency tracking unambiguous. The compiler and runtime always know exactly which signals are being read in a given context. No proxy magic, no compiler transforms of variable access.
Template Expressions
Svelte uses {variable} with implicit reactivity. AkashJS uses {signal()} with explicit signal calls.
<!-- Svelte -->
<p>{count}</p>
<!-- AkashJS -->
<p>{count()}</p>Stores Are More Structured
Svelte stores are simple writable/readable primitives. AkashJS stores have state, getters, and actions.
// Svelte
import { writable, derived } from 'svelte/store';
const count = writable(0);
const doubled = derived(count, $c => $c * 2);
function increment() {
count.update(c => c + 1);
}// AkashJS
import { defineStore } from '@akashjs/runtime';
const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
doubled: (state) => state.count() * 2,
},
actions: {
increment() {
this.count.update(c => c + 1);
},
},
});Event Handling
Svelte uses on:click. AkashJS uses onClick (JSX-style).
<!-- Svelte -->
<button on:click={increment}>+</button>
<button on:click|preventDefault={submit}>Submit</button>
<!-- AkashJS -->
<button onClick={increment}>+</button>
<button onClick={(e) => { e.preventDefault(); submit(); }}>Submit</button>Event modifiers
Svelte has built-in event modifiers (|preventDefault, |stopPropagation). AkashJS handles these in the handler function. This is more explicit but slightly more verbose.
Side-by-Side Examples
1. Counter Component
Svelte 5:
<script lang="ts">
let count = $state(0);
let doubled = $derived(count * 2);
</script>
<button onclick={() => count++}>
{count} (doubled: {doubled})
</button>AkashJS:
<script lang="ts">
import { signal, computed } from '@akashjs/runtime';
const count = signal(0);
const doubled = computed(() => count() * 2);
</script>
<template>
<button onClick={() => count.update(c => c + 1)}>
{count()} (doubled: {doubled()})
</button>
</template>2. Each/For Loops
Svelte:
{#each items as item (item.id)}
<li>{item.name}</li>
{/each}AkashJS:
<For each={items()} key={(item) => item.id}>
{(item) => <li>{item.name}</li>}
</For>3. Conditional Rendering
Svelte:
{#if isLoggedIn}
<Dashboard />
{:else}
<LoginForm />
{/if}AkashJS:
<Show when={isLoggedIn()} fallback={() => <LoginForm />}>
<Dashboard />
</Show>4. Transitions
Svelte:
<script>
import { fade } from 'svelte/transition';
let visible = $state(true);
</script>
{#if visible}
<div transition:fade>Fading content</div>
{/if}AkashJS:
<script lang="ts">
import { Transition } from '@akashjs/runtime';
import { signal } from '@akashjs/runtime';
const visible = signal(true);
</script>
<template>
<Transition name="fade" when={visible()}>
<div>Fading content</div>
</Transition>
</template>
<style>
.fade-enter-active, .fade-exit-active { transition: opacity 0.3s; }
.fade-enter-from, .fade-exit-to { opacity: 0; }
</style>5. Actions vs Directives
Svelte:
// use:clickOutside
export function clickOutside(node: HTMLElement, callback: () => void) {
const handler = (e: MouseEvent) => {
if (!node.contains(e.target as Node)) callback();
};
document.addEventListener('click', handler);
return {
destroy() {
document.removeEventListener('click', handler);
},
};
}AkashJS:
import { defineDirective } from '@akashjs/runtime';
const vClickOutside = defineDirective<() => void>({
mounted(el, { value: callback }) {
const handler = (e: MouseEvent) => {
if (!el.contains(e.target as Node)) callback();
};
document.addEventListener('click', handler);
(el as any).__clickOutsideCleanup = () =>
document.removeEventListener('click', handler);
},
unmounted(el) {
(el as any).__clickOutsideCleanup?.();
},
});AkashJS ships vClickOutside built-in
You do not need to write your own. Import it from @akashjs/runtime.
Migration Plan
Because Svelte and AkashJS are so similar, migration is mostly a syntax translation.
Phase 1: Setup (1 day)
- Add AkashJS packages to your project.
- Configure Vite with
@akashjs/vite-plugin.
Phase 2: Translate Components (1-2 weeks)
- Rename
.sveltefiles to.akash. - Add
<template>wrapper around markup. - Replace
$state()withsignal(), add()to reads. - Replace
$derived()withcomputed(). - Replace
$effect()witheffect(). - Replace
{#if}with<Show>,{#each}with<For>. - Replace
on:clickwithonClick. - Replace
<slot>withctx.children().
Phase 3: Stores (2-3 days)
- Replace Svelte writable/readable stores with
defineStore(). - The API is slightly different (state + getters + actions) but maps directly.
Phase 4: Routing (3-5 days)
- Convert SvelteKit file-based routes to AkashJS file-based routes.
+page.sveltebecomespage.akash.+layout.sveltebecomeslayout.akash.+page.server.tsload functions becomeloader.ts.
Phase 5: Remove Svelte (1 day)
- Remove
svelte,@sveltejs/kit, and related packages. - Remove Svelte-specific Vite plugin.
The shortest migration path
Svelte developers will find AkashJS the most natural transition. The mental model is almost identical -- signals, compiler, SFCs, small runtime. The main differences are explicit signal reads (count() vs count), JSX-style events (onClick vs on:click), and structured stores.