agentsclimarketplace

Role linguist svelte

Skill JasonWarrenUK/goblin-mode/skills/role-linguist-svelte

Svelte 5 and SvelteKit: runes reactivity, component composition, routing, data loading, form handling.From its SKILL.md

Install
npx -y skills add JasonWarrenUK/goblin-mode --skill role-linguist-svelte

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.8 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it

Svelte/SvelteKit Patterns

Comprehensive guide to Svelte 5 and SvelteKit development patterns. Emphasizes runes-based reactivity ($state, $derived, $effect, $props), component composition, SvelteKit routing, data loading, form handling, and performance optimization.

When This Skill Applies

Use this skill when:

  • Building Svelte components
  • Managing reactive state with runes
  • Implementing SvelteKit routes and pages
  • Creating load functions or form actions
  • Handling component composition
  • Optimizing Svelte/SvelteKit performance
  • Questions about Svelte 5 patterns or SvelteKit conventions

Svelte 5 Fundamentals

Runes Overview

Runes are compiler instructions (marked with $) that enable explicit reactivity:

  • $state - Reactive state
  • $derived - Computed values
  • $effect - Side effects
  • $props - Component props
  • $bindable - Two-way bindable props

Key principle: Reactivity is explicit, not implicit. Works in .js, .ts, and .svelte files.

Reactive State with $state

Basic State

<script>
	let count = $state(0);
	
	function increment() {
		count++; // Just a number, no wrapper needed
	}
</script>

<button onclick={increment}>
	Clicks: {count}
</button>

Key points:

  • $state creates reactive state
  • State is the value itself (not .value or getCount())
  • Updates trigger UI re-renders

Deep Reactivity

<script>
	let todos = $state([
		{ id: 1, text: 'Learn Svelte 5', done: false }
	]);
	
	function toggle(id) {
		const todo = todos.find(t => t.id === id);
		todo.done = !todo.done; // Deep reactivity works
	}
	
	function addTodo(text) {
		todos.push({ id: Date.now(), text, done: false });
		// Array methods trigger reactivity
	}
</script>

Deep reactivity:

  • Objects and arrays are automatically proxied
  • Nested mutations trigger updates
  • Array methods (.push, .splice, etc.) work reactively

State in .svelte.js Files

// counter.svelte.js
export function createCounter(initial = 0) {
	let count = $state(initial);
	
	return {
		get count() { return count; },
		increment: () => count++,
		reset: () => count = initial
	};
}
<!-- App.svelte -->
<script>
	import { createCounter } from './counter.svelte.js';
	
	const counter = createCounter(5);
</script>

<button onclick={counter.increment}>
	Count: {counter.count}
</button>

Benefits:

  • Share state logic across components
  • Testable outside Svelte components
  • No store boilerplate needed

Derived State with $derived

Basic Derivations

<script>
	let count = $state(0);
	let doubled = $derived(count * 2);
	let isEven = $derived(count % 2 === 0);
</script>

<p>{count} doubled is {doubled}</p>
<p>Count is {isEven ? 'even' : 'odd'}</p>

Key points:

  • Automatically recalculates when dependencies change
  • Memoized (only recalculates when needed)
  • Must be free of side-effects

Complex Derivations with $derived.by

<script>
	let numbers = $state([1, 2, 3, 4, 5]);
	
	let stats = $derived.by(() => {
		const total = numbers.reduce((a, b) => a + b, 0);
		const average = total / numbers.length;
		return { total, average };
	});
</script>

<p>Total: {stats.total}, Average: {stats.average}</p>

Use $derived.by when:

  • Logic doesn't fit in short expression
  • Need multiple statements
  • Complex calculations required

$derived vs $effect

$derived - Computing values (pure, returns value):

<script>
	let count = $state(0);
	let doubled = $derived(count * 2); // ✓ Good
</script>

$effect - Side effects (impure, no return value):

<script>
	let count = $state(0);
	
	$effect(() => {
		console.log('Count changed:', count); // ✓ Good
	});
</script>

Side Effects with $effect

Basic Effects

<script>
	let count = $state(0);
	
	$effect(() => {
		// Runs on mount and whenever count changes
		document.title = `Count: ${count}`;
	});
</script>

When $effect runs:

  • Initially when component mounts
  • Whenever dependencies change
  • After DOM updates (unlike Svelte 4's $:)

Effect Cleanup

<script>
	let intervalId = $state(null);
	let elapsed = $state(0);
	
	$effect(() => {
		const id = setInterval(() => {
			elapsed++;
		}, 1000);
		
		// Cleanup runs when effect re-runs or component unmounts
		return () => clearInterval(id);
	});
</script>

$effect.pre (Before DOM Update)

<script>
	let messages = $state([]);
	let div;
	
	$effect.pre(() => {
		const isAtBottom = 
			div.scrollHeight - div.scrollTop === div.clientHeight;
		
		if (isAtBottom) {
			// After DOM updates, scroll to bottom
			$effect(() => {
				div.scrollTop = div.scrollHeight;
			});
		}
	});
</script>

Common $effect Patterns

Local storage sync:

<script>
	let preferences = $state(JSON.parse(
		localStorage.getItem('prefs') || '{}'
	));
	
	$effect(() => {
		localStorage.setItem('prefs', JSON.stringify(preferences));
	});
</script>

API calls:

<script>
	let userId = $state('123');
	let user = $state(null);
	
	$effect(() => {
		fetch(`/api/users/${userId}`)
			.then(r => r.json())
			.then(data => user = data);
	});
</script>

Avoid $effect for:

  • Derived values (use $derived)
  • DOM manipulation Svelte handles (bindings, directives)
  • Setting document.title (use <svelte:head>)

Component Props with $props

Basic Props

<!-- Button.svelte -->
<script>
	let { label, variant = 'primary' } = $props();
</script>

<button class="btn btn--{variant}">
	{label}
</button>

Usage:

<Button label="Click me" variant="secondary" />

Props with TypeScript

<script lang="ts">
	interface Props {
		label: string;
		variant?: 'primary' | 'secondary' | 'danger';
		onclick?: () => void;
	}
	
	let { label, variant = 'primary', onclick }: Props = $props();
</script>

<button class="btn btn--{variant}" {onclick}>
	{label}
</button>

Rest Props

<script>
	let { label, ...rest } = $props();
</script>

<button {...rest}>
	{label}
</button>

Bindable Props with $bindable

<!-- Input.svelte -->
<script>
	let { value = $bindable('') } = $props();
</script>

<input bind:value />

Usage:

<script>
	let text = $state('');
</script>

<Input bind:value={text} />
<p>You typed: {text}</p>

Additional resources

Deep-dive detail lives in supporting files, loaded only when needed:

  • component-patterns.md — slot patterns (basic, named, slot props) and composition (compound components, higher-order components)
  • sveltekit-routing-and-data.md — file-based routing, dynamic/optional route params, load functions (client and server), streaming, form actions
  • performance-and-common-patterns.md — lazy loading, list virtualisation, memoisation, keyed each blocks, form validation, modal management, debounced input

Anti-Patterns

Don't: Use $effect for Derived Values

<!-- ✗ Bad -->
<script>
	let count = $state(0);
	let doubled = $state(0);
	
	$effect(() => {
		doubled = count * 2; // Wrong! Use $derived
	});
</script>

<!-- ✓ Good -->
<script>
	let count = $state(0);
	let doubled = $derived(count * 2);
</script>

Don't: Mutate Props Directly

<!-- ✗ Bad -->
<script>
	let { user } = $props();
	
	function updateName() {
		user.name = 'New Name'; // Wrong! Props are read-only
	}
</script>

<!-- ✓ Good -->
<script>
	let { user, onUpdate } = $props();
	
	function updateName() {
		onUpdate({ ...user, name: 'New Name' });
	}
</script>

Don't: Create Unnecessary $state

<!-- ✗ Bad -->
<script>
	let count = $state(0);
	let doubled = $state(0);
	let isEven = $state(false);
	
	function increment() {
		count++;
		doubled = count * 2;     // Derived!
		isEven = count % 2 === 0; // Derived!
	}
</script>

<!-- ✓ Good -->
<script>
	let count = $state(0);
	let doubled = $derived(count * 2);
	let isEven = $derived(count % 2 === 0);
</script>

Success Criteria

Svelte/SvelteKit code is well-structured when:

  • Runes used appropriately ($state for state, $derived for computed, $effect for side effects)
  • Components are composable and reusable
  • Load functions fetch data efficiently
  • Forms use progressive enhancement
  • Performance optimized (lazy loading, memoization)
  • TypeScript types are accurate
  • Code is maintainable and follows Svelte 5 conventions

What ships with it: 3 files

8.7 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.