Desarrollo con Svelte y Claude Code
Aprenda sobre desarrollo con Svelte usando Claude Code. Incluye ejemplos practicos de codigo.
Svelte開発でClaude Codeを使うメリット
Svelteはコンパイラベースのフレームワークで、少ないボイラープレートで高性能なUIを構築できます。Claude Codeはリアクティブ宣言やSvelteKit固有のルーティング・データロードのパターンを理解し、効率的なコードを生成できます。
Svelteコンポーネントの生成
> Svelteでモーダル付きの画像ギャラリーコンポーネントを作成して。
> グリッド表示、クリックで拡大、キーボードナビゲーション対応で。
<!-- ImageGallery.svelte -->
<script lang="ts">
interface Image {
id: string;
src: string;
alt: string;
thumbnail: string;
}
export let images: Image[] = [];
let selectedIndex = -1;
let isModalOpen = false;
$: selectedImage = selectedIndex >= 0 ? images[selectedIndex] : null;
function openModal(index: number) {
selectedIndex = index;
isModalOpen = true;
}
function closeModal() {
isModalOpen = false;
selectedIndex = -1;
}
function navigate(direction: number) {
selectedIndex = (selectedIndex + direction + images.length) % images.length;
}
function handleKeydown(e: KeyboardEvent) {
if (!isModalOpen) return;
switch (e.key) {
case 'Escape': closeModal(); break;
case 'ArrowLeft': navigate(-1); break;
case 'ArrowRight': navigate(1); break;
}
}
</script>
<svelte:window on:keydown={handleKeydown} />
<div class="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
{#each images as image, i}
<button
class="overflow-hidden rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
on:click={() => openModal(i)}
>
<img
src={image.thumbnail}
alt={image.alt}
class="h-48 w-full object-cover transition-transform hover:scale-105"
loading="lazy"
/>
</button>
{/each}
</div>
{#if isModalOpen && selectedImage}
<div
class="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
on:click={closeModal}
role="dialog"
aria-modal="true"
aria-label="画像拡大表示"
>
<div class="relative max-h-[90vh] max-w-[90vw]" on:click|stopPropagation>
<img
src={selectedImage.src}
alt={selectedImage.alt}
class="max-h-[90vh] max-w-[90vw] object-contain"
/>
<button
class="absolute -left-12 top-1/2 -translate-y-1/2 rounded-full bg-white/20 p-2 text-white hover:bg-white/40"
on:click={() => navigate(-1)}
aria-label="前の画像"
>
←
</button>
<button
class="absolute -right-12 top-1/2 -translate-y-1/2 rounded-full bg-white/20 p-2 text-white hover:bg-white/40"
on:click={() => navigate(1)}
aria-label="次の画像"
>
→
</button>
<button
class="absolute -top-10 right-0 text-white hover:text-gray-300"
on:click={closeModal}
aria-label="Close"
>
Close
</button>
</div>
<p class="absolute bottom-4 text-center text-white">
{selectedIndex + 1} / {images.length}
</p>
</div>
{/if}
Svelteストアの設計
> Svelteのストアでテーマとユーザー設定を管理して。
> localStorage永続化付きで。
// stores/settings.ts
import { writable, derived } from 'svelte/store';
import { browser } from '$app/environment';
interface UserSettings {
theme: 'light' | 'dark' | 'system';
language: 'ja' | 'en';
fontSize: 'sm' | 'md' | 'lg';
sidebarCollapsed: boolean;
}
const defaultSettings: UserSettings = {
theme: 'system',
language: 'ja',
fontSize: 'md',
sidebarCollapsed: false,
};
function createSettingsStore() {
const initial = browser
? JSON.parse(localStorage.getItem('settings') || 'null') || defaultSettings
: defaultSettings;
const { subscribe, set, update } = writable<UserSettings>(initial);
return {
subscribe,
update(partial: Partial<UserSettings>) {
update(current => {
const next = { ...current, ...partial };
if (browser) localStorage.setItem('settings', JSON.stringify(next));
return next;
});
},
reset() {
set(defaultSettings);
if (browser) localStorage.removeItem('settings');
},
};
}
export const settings = createSettingsStore();
// 派生ストア:実際に適用されるテーマ
export const effectiveTheme = derived(settings, ($settings) => {
if ($settings.theme !== 'system') return $settings.theme;
if (!browser) return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
});
SvelteKitのサーバーサイド機能
> SvelteKitでブログのデータロードとフォームアクションを実装して。
// src/routes/blog/[slug]/+page.server.ts
import type { PageServerLoad, Actions } from './$types';
import { error, fail } from '@sveltejs/kit';
import { prisma } from '$lib/server/db';
export const load: PageServerLoad = async ({ params }) => {
const post = await prisma.post.findUnique({
where: { slug: params.slug },
include: {
author: { select: { name: true, avatar: true } },
comments: {
include: { author: { select: { name: true } } },
orderBy: { createdAt: 'desc' },
},
},
});
if (!post) throw error(404, 'Post not found');
return { post };
};
export const actions: Actions = {
comment: async ({ request, params, locals }) => {
const session = await locals.getSession();
if (!session) return fail(401, { message: 'ログインが必要です' });
const formData = await request.formData();
const body = formData.get('body')?.toString();
if (!body || body.length < 2) {
return fail(400, { message: 'コメントは2文字以上入力してください', body });
}
await prisma.comment.create({
data: {
body,
postSlug: params.slug,
authorId: session.user.id,
},
});
return { success: true };
},
};
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData, ActionData } from './$types';
import { enhance } from '$app/forms';
export let data: PageData;
export let form: ActionData;
</script>
<article class="prose mx-auto max-w-3xl">
<h1>{data.post.title}</h1>
<p class="text-gray-500">
{data.post.author.name} - {new Date(data.post.createdAt).toLocaleDateString('en-US')}
</p>
{@html data.post.content}
</article>
<section class="mx-auto mt-12 max-w-3xl">
<h2 class="text-xl font-bold">コメント ({data.post.comments.length})</h2>
<form method="POST" action="?/comment" use:enhance>
<textarea
name="body"
rows="3"
class="mt-4 w-full rounded border p-3"
placeholder="コメントを入力..."
value={form?.body ?? ''}
/>
{#if form?.message}
<p class="text-sm text-red-600">{form.message}</p>
{/if}
<button type="submit" class="mt-2 rounded bg-blue-600 px-4 py-2 text-white">
送信
</button>
</form>
{#each data.post.comments as comment}
<div class="mt-4 rounded border p-4">
<p class="font-medium">{comment.author.name}</p>
<p class="mt-1 text-gray-700">{comment.body}</p>
</div>
{/each}
</section>
テスト
import { render, fireEvent } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import ImageGallery from './ImageGallery.svelte';
describe('ImageGallery', () => {
const images = [
{ id: '1', src: '/img1.jpg', alt: 'Image 1', thumbnail: '/thumb1.jpg' },
{ id: '2', src: '/img2.jpg', alt: 'Image 2', thumbnail: '/thumb2.jpg' },
];
it('should render all images', () => {
const { getAllByRole } = render(ImageGallery, { props: { images } });
expect(getAllByRole('button')).toHaveLength(2);
});
it('should open modal on click', async () => {
const { getAllByRole, getByRole } = render(ImageGallery, { props: { images } });
await fireEvent.click(getAllByRole('button')[0]);
expect(getByRole('dialog')).toBeTruthy();
});
});
Summary
Claude Codeを使えば、Svelte / SvelteKitのコンポーネント、ストア、サーバーサイドロジックを効率的に開発できます。Svelteのリアクティブ宣言やSvelteKit固有のload関数・フォームアクションもClaude Codeは正確に扱えます。プロンプトの工夫で品質を高めるコツはプロンプトテクニック完全ガイドを参照してください。フレームワーク比較に興味がある方はClaude Code vs GitHub Copilotもご覧ください。
Claude Codeの詳細はAnthropic公式ドキュメントをご覧ください。SvelteKitの公式ガイドはSvelteKit公式サイトを参照してください。
PDF gratuito: Hoja de trucos de Claude Code en 5 minutos
Solo deja tu correo y te enviaremos al instante la hoja de trucos en una página A4.
Cuidamos tus datos personales y nunca enviamos spam.
Sobre el autor
Masa
Ingeniero apasionado por Claude Code. Dirige claudecode-lab.com, un medio tecnológico en 10 idiomas con más de 2.000 páginas.
Artículos relacionados
7 comprobaciones antes de publicar cada día un artículo multilingüe sobre Claude Code
Una lista práctica para publicar artículos multilingües sobre Claude Code todos los días sin olvidar idiomas, romper CTAs ni dejar páginas antiguas en producción.
Que es Codex Automations y como dejar que la IA gestione contenido mientras duermes
Guia practica para usar Codex Automations en analitica, articulos, CTA, despliegue y monetizacion.
Claude Code × GCP Cloud Functions Guía Completa | Desarrollo Serverless Ultrarrápido
Optimiza GCP Cloud Functions con Claude Code. Implementa triggers HTTP/Pub/Sub/Firestore, pruebas locales y automatización de despliegues con ejemplos de código reales de la experiencia de Masa.