소스 정보
- 저장소
- marcus/marcus-skills
- 최근 소스 활동
- 2026년 8월 8일 03:35
- 감지된 SKILL.md 언어
- 영어
- 스타
- 3
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/marcus/marcus-skills --skill sveltekit-latest명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
SOC 직업 분류 기준
| name | sveltekit-latest |
| description | Quick-reference for SvelteKit + Svelte 5 development (Feb 2026) |
| version | 1.0.0 |
| tags | ["svelte","sveltekit","frontend","typescript","runes"] |
| Package | Version | Notes |
|---|---|---|
| Svelte | ~5.50.x | Svelte 5 is the current stable. Svelte 4 is legacy. |
| SvelteKit | ~2.50.x | SvelteKit 2 is current stable. SvelteKit 1 is legacy. |
| sv (CLI) | latest | Replaces create-svelte. Use npx sv for all scaffolding. |
# Current recommended command (npm create svelte@latest is DEPRECATED)
npx sv create my-app
cd my-app
npm install
npm run dev
| Flag | Purpose |
|---|---|
--template minimal|demo|library | Project template |
--types ts|jsdoc|--no-types | TypeScript config |
--add [add-ons...] | Install add-ons (see below) |
--no-add-ons | Skip interactive add-on prompt |
--install npm|pnpm|yarn|bun|deno | Package manager |
drizzle, eslint, better-auth, tailwindcss, prettier, vitest, playwright, storybook, mdsvex, paraglide, sveltekit-adapter, mcp, devtools-json
TypeScript is built-in. Select "ts" when prompted by npx sv create, or pass --types ts. No extra configuration needed.
<script lang="ts"> in .svelte filesnpm run dev or npx svelte-kit syncverbatimModuleSyntax: true, isolatedModules: true, noEmit: true.svelte.ts files for reactive modules (replaces .ts when you need runes)Runes replace Svelte 4's implicit reactivity (let, $:) with explicit, portable primitives.
<script>
let count = $state(0); // primitive
let todos = $state([]); // deep reactive proxy (arrays/objects)
</script>
<button onclick={() => count++}>{count}</button>
done = $state(false); inside class bodieslet items = $state.raw([1, 2, 3]);
// items.push(4) -- NO EFFECT (not reactive)
items = [...items, 4]; // works (reassignment)
Use for large collections you never mutate in place. Better performance.
let data = $state({ count: 0 });
console.log($state.snapshot(data)); // plain object, no proxy
Use when passing state to external libraries or structuredClone.
let count = $state(0);
const doubled = $derived(count * 2);
const filtered = $derived.by(() => {
return items.filter(item => item.active);
});
$derived(expr) is equivalent to $derived.by(() => expr).
$effect(() => {
// Runs on mount and whenever dependencies change
// Dependencies are auto-tracked
console.log(`count is ${count}`);
// Optional cleanup (returned function runs before re-execution)
return () => { /* cleanup */ };
});
$effect.pre(() => {
// Runs before DOM is updated. Rare use case.
});
Rule of thumb: $derived for values, $effect for actions/side effects.
<script>
// Replaces "export let"
let { name, count = 0, class: klass, ...rest } = $props();
</script>
<script>
let { value = $bindable('default') } = $props();
</script>
Parent can then use bind:value={something}.
$inspect(count); // logs to console whenever count changes (dev only, stripped in prod)
Slots are deprecated. Use snippets and {@render} instead.
<!-- Parent -->
<Card>
<p>This becomes the children snippet</p>
</Card>
<!-- Card.svelte -->
<script>
let { children } = $props();
</script>
<div class="card">
{@render children?.()}
</div>
<!-- Parent -->
<Card>
{#snippet header()}
<h2>Title</h2>
{/snippet}
{#snippet footer()}
<p>Footer</p>
{/snippet}
<p>Default children content</p>
</Card>
<!-- Card.svelte -->
<script>
let { header, footer, children } = $props();
</script>
<div class="card">
{@render header?.()}
{@render children?.()}
{@render footer?.()}
</div>
<!-- Parent -->
<List items={users}>
{#snippet item(user)}
<span>{user.name}</span>
{/snippet}
</List>
<!-- List.svelte -->
<script>
let { items, item } = $props();
</script>
{#each items as entry}
{@render item(entry)}
{/each}
<!-- Svelte 4 -->
<button on:click={handler}>Click</button>
<input on:input={handler} />
<!-- Svelte 5 -->
<button onclick={handler}>Click</button>
<input oninput={handler} />
<!-- Svelte 4 child -->
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
</script>
<button on:click={() => dispatch('submit', data)}>Submit</button>
<!-- Svelte 5 child -->
<script>
let { onsubmit } = $props();
</script>
<button onclick={() => onsubmit(data)}>Submit</button>
Replace on:click|preventDefault with explicit calls:
<button onclick={(e) => { e.preventDefault(); handler(e); }}>Click</button>
// Svelte 4
const app = new App({ target: document.getElementById('app') });
// Svelte 5
import { mount, unmount } from 'svelte';
const app = mount(App, { target: document.getElementById('app') });
// later: unmount(app);
// counter.svelte.ts
let count = $state(0);
// Cannot export reassignable $state directly. Two patterns:
// Pattern 1: Export object (mutate properties)
export const counter = $state({ count: 0 });
export function increment() { counter.count++; }
// Pattern 2: Export getter functions
export function getCount() { return count; }
export function increment() { count++; }
SSR caveat: Module-scoped
$stateis a singleton shared across SSR requests. Always reset at render start. See "SSR Behavior for Module-Scoped State" below.
| Context | SSR | CSR |
|---|---|---|
Top-level <script> code | YES | YES |
$state initialization | YES | YES |
$derived / $derived.by | Compiled as IIFE (computed once at module init) | Reactive (recomputes on dependency change) |
$effect / $effect.pre | NO | YES |
onMount | NO | YES |
| Template expressions | YES | YES |
$derived.by(() => ...) compiles to const x = (() => ...)() during SSR -- an immediately-invoked function that runs once at module initialization, not reactively. If module-scoped $state is set after the derived is initialized, the derived will NOT reflect the updated values.
Broken pattern:
// store.svelte.ts
let page = $state('');
const snapshot = $derived.by(() => ({ page })); // Frozen at init during SSR!
function setPage(p) { page = p; }
Working pattern:
// store.svelte.ts
let page = $state('');
function buildSnapshot() { return { page }; } // Fresh read every call
export const store = {
get snapshot() { return buildSnapshot(); }, // Works in both SSR and CSR
setPage(p) { page = p; },
};
During CSR, reading $state variables inside a getter called from a template still triggers Svelte 5's fine-grained reactivity tracking.
To populate state during SSR, call setters at the top level of <script> blocks (not in $effect or onMount). Use $effect for client-side reactive updates:
<script>
import { page } from '$app/stores';
import { myStore } from '$lib/stores/my.svelte';
let { data } = $props();
// SSR + CSR: runs on every render
myStore.setPage(data.title);
// CSR only: reactive updates on navigation
$effect(() => {
myStore.setPage(data.title);
});
</script>
When you need $derived values for SSR baseline code, DON'T read the $derived at top level. Instead, compute the value directly from the source:
<!-- BAD: warns because $derived is read outside reactive context -->
<script>
let isAdmin = $derived(data.user?.role === 'admin');
if (isAdmin) { store.setAuth({...}); } // Warning!
</script>
<!-- GOOD: compute from source directly -->
<script>
let isAdmin = $derived(data.user?.role === 'admin');
// SSR baseline: use source data, not $derived
if (data.user?.role === 'admin') { store.setAuth({...}); }
// $effect for CSR reactivity uses $derived normally
$effect(() => { if (isAdmin) { store.setAuth({...}); } });
</script>
Module-scoped $state is shared across all SSR requests (it's a singleton). SvelteKit SSR is synchronous per-request, so call reset() at the top of the root layout to prevent cross-request pollution:
<!-- +layout.svelte (root) -->
<script>
import { myStore } from '$lib/stores/my.svelte';
myStore.reset(); // Clear state before each render
</script>
error() and redirect() no longer need to be thrown -- just call themcookies.set/delete/serialize require explicit path parametervitePreprocess from @sveltejs/vite-plugin-svelte (not from SvelteKit)resolvePath replaced by resolveRoute from $app/pathspaths.relative config# Migrate Svelte 4 -> Svelte 5
npx sv migrate svelte-5
# Migrate SvelteKit 1 -> SvelteKit 2
npx sv migrate sveltekit-2
src/
lib/ # Shared code, importable as $lib/
routes/ # File-based routing
+page.svelte # Page component
+page.ts # Page load function (universal)
+page.server.ts # Server-only load / form actions
+layout.svelte # Layout component
+layout.ts # Layout load function
+layout.server.ts # Server-only layout load
+error.svelte # Error page
+server.ts # API endpoint (GET, POST, etc.)
app.html # HTML template
app.d.ts # Auto-generated types
svelte.config.js # SvelteKit configuration
vite.config.ts # Vite configuration
<!-- +page.svelte -->
<form method="POST" action="?/create">
<input name="title" />
<button>Create</button>
</form>
// +page.server.ts
import type { Actions } from './$types';
export const actions = {
create: async ({ request }) => {
const data = await request.formData();
const title = data.get('title');
// handle creation
}
} satisfies Actions;
// +page.ts (runs on server AND client)
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch }) => {
const res = await fetch('/api/data');
return { items: await res.json() };
};
// +page.server.ts (runs ONLY on server)
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
return { user: locals.user };
};