{prompt.title}
\n \n {#if variant !== 'compact'}\n{prompt.description}
\n {/if}\n \n \n \n {#if showAuthor && prompt.author}\n \n {/if}\n \nCount: {count}
\n \n \n \nSubtotal: ${subtotal.toFixed(2)}
\nTax: ${tax.toFixed(2)}
\nTotal: {formattedTotal}
\n{error}
\n {:else if isLoading}\nSearching...
\n {:else if results.length > 0}\n{result.description}
\n \nNo results found
\n {/if}\n{prompt.description}
\n {/if}\n \n \n \n {#if showAuthor && prompt.author}\n \n {/if}\n \nAre you sure you want to continue?
\n \n \nReactive state management with Svelte 5 runes for Google Antigravity projects including $state, $derived, and $effect patterns.
# Svelte 5 Runes State Management for Google Antigravity
Master reactive state management with Svelte 5 runes in your Google Antigravity IDE projects. This comprehensive guide covers $state, $derived, $effect, and component patterns optimized for Gemini 3 agentic development.
## Basic State with $state
Create reactive state using the $state rune:
```svelte
<!-- components/Counter.svelte -->
<script lang="ts">
let count = $state(0);
let name = $state('World');
function increment() {
count++;
}
function decrement() {
count--;
}
</script>
<div class="counter">
<h2>Hello, {name}!</h2>
<p>Count: {count}</p>
<button onclick={decrement}>-</button>
<button onclick={increment}>+</button>
<input bind:value={name} placeholder="Your name" />
</div>
```
## Derived State with $derived
Compute values that depend on other state:
```svelte
<!-- components/Cart.svelte -->
<script lang="ts">
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
let items = $state<CartItem[]>([
{ id: '1', name: 'Prompt Pack', price: 9.99, quantity: 1 },
{ id: '2', name: 'MCP Server', price: 19.99, quantity: 2 },
]);
// Derived values automatically update
let itemCount = $derived(items.reduce((sum, item) => sum + item.quantity, 0));
let subtotal = $derived(items.reduce((sum, item) => sum + item.price * item.quantity, 0));
let tax = $derived(subtotal * 0.1);
let total = $derived(subtotal + tax);
// Derived with complex logic
let formattedTotal = $derived.by(() => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(total);
});
function updateQuantity(id: string, delta: number) {
const item = items.find((i) => i.id === id);
if (item) {
item.quantity = Math.max(0, item.quantity + delta);
if (item.quantity === 0) {
items = items.filter((i) => i.id !== id);
}
}
}
function removeItem(id: string) {
items = items.filter((i) => i.id !== id);
}
</script>
<div class="cart">
<h2>Shopping Cart ({itemCount} items)</h2>
{#each items as item (item.id)}
<div class="cart-item">
<span>{item.name}</span>
<span>${item.price.toFixed(2)}</span>
<div class="quantity">
<button onclick={() => updateQuantity(item.id, -1)}>-</button>
<span>{item.quantity}</span>
<button onclick={() => updateQuantity(item.id, 1)}>+</button>
</div>
<button onclick={() => removeItem(item.id)}>Remove</button>
</div>
{/each}
<div class="totals">
<p>Subtotal: ${subtotal.toFixed(2)}</p>
<p>Tax: ${tax.toFixed(2)}</p>
<p class="total">Total: {formattedTotal}</p>
</div>
</div>
```
## Side Effects with $effect
Handle side effects reactively:
```svelte
<!-- components/Search.svelte -->
<script lang="ts">
import { debounce } from '@/lib/utils';
interface SearchResult {
id: string;
title: string;
description: string;
}
let query = $state('');
let results = $state<SearchResult[]>([]);
let isLoading = $state(false);
let error = $state<string | null>(null);
// Effect runs when dependencies change
$effect(() => {
if (query.length < 2) {
results = [];
return;
}
const controller = new AbortController();
async function search() {
isLoading = true;
error = null;
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
signal: controller.signal,
});
if (!response.ok) throw new Error('Search failed');
const data = await response.json();
results = data.results;
} catch (e) {
if (e instanceof Error && e.name !== 'AbortError') {
error = e.message;
}
} finally {
isLoading = false;
}
}
// Debounce search
const timeoutId = setTimeout(search, 300);
// Cleanup function runs before next effect
return () => {
clearTimeout(timeoutId);
controller.abort();
};
});
// Effect for localStorage sync
$effect(() => {
localStorage.setItem('lastSearch', query);
});
// Pre-effect runs before DOM updates
$effect.pre(() => {
console.log('About to update with', results.length, 'results');
});
</script>
<div class="search">
<input
type="search"
bind:value={query}
placeholder="Search prompts..."
class:loading={isLoading}
/>
{#if error}
<p class="error">{error}</p>
{:else if isLoading}
<p>Searching...</p>
{:else if results.length > 0}
<ul class="results">
{#each results as result (result.id)}
<li>
<a href="/prompts/{result.id}">
<strong>{result.title}</strong>
<p>{result.description}</p>
</a>
</li>
{/each}
</ul>
{:else if query.length >= 2}
<p>No results found</p>
{/if}
</div>
```
## Props with $props
Define component props with runes:
```svelte
<!-- components/PromptCard.svelte -->
<script lang="ts">
import type { Prompt } from '@/lib/types';
interface Props {
prompt: Prompt;
variant?: 'default' | 'compact' | 'featured';
showAuthor?: boolean;
onStar?: (id: string) => void;
}
let {
prompt,
variant = 'default',
showAuthor = true,
onStar,
}: Props = $props();
let isStarred = $state(false);
async function handleStar() {
isStarred = !isStarred;
onStar?.(prompt.id);
}
</script>
<article class="prompt-card" class:compact={variant === 'compact'} class:featured={variant === 'featured'}>
<h3>{prompt.title}</h3>
{#if variant !== 'compact'}
<p>{prompt.description}</p>
{/if}
<div class="tags">
{#each prompt.tags as tag}
<span class="tag">{tag}</span>
{/each}
</div>
{#if showAuthor && prompt.author}
<div class="author">
<img src={prompt.author.image} alt={prompt.author.name} />
<span>{prompt.author.name}</span>
</div>
{/if}
<div class="actions">
<button onclick={handleStar} class:starred={isStarred}>
{isStarred ? '★' : '☆'} {prompt.starCount}
</button>
</div>
</article>
```
## Bindable Props with $bindable
Create two-way binding props:
```svelte
<!-- components/Modal.svelte -->
<script lang="ts">
interface Props {
open: boolean;
title: string;
children: import('svelte').Snippet;
onClose?: () => void;
}
let { open = $bindable(false), title, children, onClose }: Props = $props();
function close() {
open = false;
onClose?.();
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') close();
}
</script>
<svelte:window onkeydown={handleKeydown} />
{#if open}
<div class="modal-backdrop" onclick={close}>
<div class="modal" onclick={(e) => e.stopPropagation()}>
<header>
<h2>{title}</h2>
<button onclick={close} aria-label="Close">×</button>
</header>
<div class="modal-content">
{@render children()}
</div>
</div>
</div>
{/if}
<!-- Usage -->
<script>
let showModal = $state(false);
</script>
<button onclick={() => showModal = true}>Open Modal</button>
<Modal bind:open={showModal} title="Confirm Action">
<p>Are you sure you want to continue?</p>
<button onclick={() => showModal = false}>Cancel</button>
<button onclick={confirm}>Confirm</button>
</Modal>
```
## Stores with Runes
Create shared state stores:
```typescript
// stores/user.svelte.ts
import type { User } from '@/lib/types';
class UserStore {
user = $state<User | null>(null);
isLoading = $state(false);
isAuthenticated = $derived(this.user !== null);
displayName = $derived(this.user?.name ?? 'Guest');
async login(email: string, password: string) {
this.isLoading = true;
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
this.user = await response.json();
} finally {
this.isLoading = false;
}
}
logout() {
this.user = null;
}
}
export const userStore = new UserStore();
```
## Best Practices
1. **Use $state for reactive values** that change over time
2. **Use $derived for computed values** that depend on state
3. **Use $effect for side effects** like API calls and subscriptions
4. **Clean up effects** by returning cleanup functions
5. **Use $props for component inputs** with TypeScript types
6. **Create stores as classes** with $state properties
7. **Avoid unnecessary effects** - prefer derived state when possibleThis svelte prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their svelte implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For svelte projects, consider mentioning your framework version, coding style, and any specific libraries you're using.