Open10

SvelteKit環境構築メモ

ryo13chanryo13chan

プロジェクト作成

# bunを使用して最新バージョンでプロジェクト作成
$ bun create svelte@latest svelte-app
# 真っ新な環境が欲しいのでSkeletonで
  Welcome to SvelteKit!
│
◆  Which Svelte app template?
│  ○ SvelteKit demo app
│  ● Skeleton project (Barebones scaffolding for your new SvelteKit app)
│  ○ Library project
# TypeScriptは使う
  Add type checking with TypeScript?
│  ● Yes, using TypeScript syntax
│  ○ Yes, using JavaScript with JSDoc comments
│  ○ No
# 一旦何も入れない
# Svelte5だけ使いたいのでON
 Select additional options (use arrow keys/space bar)
│  ◻ Add ESLint for code linting
│  ◻ Add Prettier for code formatting
│  ◻ Add Playwright for browser testing
│  ◻ Add Vitest for unit testing
│  ◼ Try the Svelte 5 preview (unstable!)
$ cd svelte-app
# インストール
$ bun install
# 起動
$ bun run dev

http://localhost:5173/
にアクセスして起動されていることを確認

ryo13chanryo13chan

起動時に自動でブラウザで開くようにしておく

package.json
"scripts": {
  "dev": "vite dev --open"
}

ryo13chanryo13chan

VSCode拡張

  • Svelte for VS Code
  • svelte
  • Svelte Intellisense
  • Svelte 3 Snippets
ryo13chanryo13chan

Biomeを導入

# インストール
$ bun add --dev --exact @biomejs/biome
# 設定ファイル作成
$ bunx biome init

Svelteの場合、コンパイルエラーを防ぐために設定ファイルに以下を書いておくと良いらしい

biome.json
  "overrides": [
    {
      "include": ["*.svelte"],
      "linter": {
        "rules": {
          "style": {
            "useConst": "off"
          }
        }
      }
    }
  ]

https://biomejs.dev/ja/internals/language-support/#:~:text=.svelte ファイルを静的解析する際、コンパイラエラーを防ぐために useConst をオフにすることをお勧めします。オプション overrides を使用します:

VSCodeの拡張機能

  • Biome

VSCode設定

settings.json
{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

noUnusedImports を設定すると、shadcnで追加したコンポーネントが未使用扱いで消えてしまうので、一旦未設定

ryo13chanryo13chan

TailwindCSSを導入

# インストール
$ bun add -D tailwindcss postcss autoprefixer
# 設定ファイル作成
$ bun tailwindcss init -p
tailwind.config.js
// 追加
content: ['./src/**/*.{html,js,svelte,ts}'],

CSSファイル作成

app.css
@tailwind base;
@tailwind components;
@tailwind utilities;

layoutファイルで全体に読み込み

+layout.svelte
<script lang="ts">
import '../app.css'
</script>

反映されることを確認

+page.svelte
<h1 class="text-3xl font-bold underline">
  Hello world!
</h1>

ryo13chanryo13chan

UIライブラリ導入
shadcn-svelteを使用

# インストール
$ bunx  shadcn-svelte@latest init

全てデフォルトで

◆  Which style would you like to use?
│  Default

◆  Which base color would you like to use?
│  ● Slate
│  ○ Gray
│  ○ Zinc
│  ○ Neutral
│  ○ Stone

◆  Where is your global CSS file? (this file will be overwritten)
│  src/app.css

◆  Where is your Tailwind config located? (this file will be overwritten)
│  tailwind.config.js

◆  Configure the import alias for components:
│  $lib/components

◆  Configure the import alias for utils:
│  $lib/utils

VSCode拡張

  • shadcn/svelte

使用したいコンポーネントを追加

$ bunx shadcn-svelte@latest add button
 Ready to install components and dependencies?
│  ● Yes / ○ No

表示されることを確認

+page.svelte
<script lang="ts">
import { Button } from '$lib/components/ui/button'
</script>

<Button>Click me</Button>

ryo13chanryo13chan

アイコンライブラリ導入
svelte-material-icons Vercelでビルドエラー発生したので他のライブラリ検討中

# インストール
$ bun add svelte-material-icons

表示されることを確認

+page.svelte
<script lang="ts">
import Check from 'svelte-material-icons/Check.svelte'
</script>

<Check size="24" />

ryo13chanryo13chan

フォームライブラリ導入
Zod
Superforms

# インストール
$ bun add -D sveltekit-superforms zod

以下ログイン画面を例に

スキーマファイルを作成

$lib/schemas/login.ts
import { z } from 'zod'

export const loginSchema = z.object({
  email: z.string().email(),
  password: z.string(),
})

export type LoginSchema = typeof loginSchema

サーバーサイドでフォームを初期化

login/+page.server.ts
import { loginSchema } from '$lib/schemas/login'
import { superValidate } from 'sveltekit-superforms'
import { zod } from 'sveltekit-superforms/adapters'
import type { PageServerLoad } from './$types'

export const load: PageServerLoad = async () => {
  const form = await superValidate(zod(loginSchema))

  return { form }
}

クライアントサイドで使用

login/+page.svelte
<script lang="ts">
import Button from '$lib/components/ui/button/button.svelte'
import { Input } from '$lib/components/ui/input'
import { superForm } from 'sveltekit-superforms'
import type { PageData } from './$types'

let {
  data,
}: {
  data: PageData
} = $props()

const { form } = superForm(data.form)
</script>


<form method="POST">
  <Input type="email" name="email" placeholder="email" class="max-w-xs mb-4" bind:value={$form.email} />
  <Input type="password" name="password" placeholder="password" class="max-w-xs mb-4" bind:value={$form.password} />
  <Button type="submit">Submit</Button>
</form>

サーバーサイドで送信された値を元にログイン処理

login/+page.server.ts
export const actions: Actions = {
  default: async (event) => {
    const form = await superValidate(event, zod(loginSchema))
    if (!form.valid) {
      return fail(400, { form })
    }

    // ログイン処理

    return message(form, 'ログインしました')
  },
}

messageを受け取って成功したら表示

login/+page.svelte
const { form, message } = superForm(data.form)

{#if $message}
  <div>{$message}</div>
{/if}

エラーメッセージの表示

$lib/schemas/login.ts
// 8文字以上を追加
password: z.string().min(8),
login/+page.svelte
const { form, errors, message } = superForm(data.form)

{#if $errors.password}
  <div>{ $errors.password }</div>
{/if}

ryo13chanryo13chan

フラッシュメッセージのライブラリ導入
sveltekit-flash-message

# インストール
$ bun add -D sveltekit-flash-message
# トーストのコンポーネントも合わせて入れておく
$ bunx shadcn-svelte@latest add sonner

型定義を追加

app.d.ts
namespace App {
  interface PageData {
    flash?: { type: 'success' | 'error'; message: string }
  }
}

トップレベルのレイアウトファイルのloadを上書き

routes/+layout.server.ts
export { load } from 'sveltekit-flash-message/server'

トップレベルのレイアウトファイルで受け取ったメッセージを表示

routes/+layout.svelte
<script lang="ts">
import { page } from '$app/stores'
import { Toaster } from '$lib/components/ui/sonner'
import { toast } from 'svelte-sonner'
import { getFlash } from 'sveltekit-flash-message'

const flash = getFlash(page)
$effect(() => {
  if (!$flash) return
  switch ($flash.type) {
    case 'success':
      toast.success($flash.message)
      break
    case 'error':
      toast.error($flash.message)
      break
    default:
      break
  }
})
</script>

<Toaster position="top-right" />

実際にメッセージを送信してみる
上記のログイン機能に導入

routes/login/+page.server.ts
import { redirect } from 'sveltekit-flash-message/server'

// 成功後、メッセージを表示してホームへリダイレクト
return redirect('/', { message: 'ログインしました', type: 'success' }, event)

右上に表示されることを表示

エラーメッセージの表示

routes/login/+page.server.ts
import { fail } from '@sveltejs/kit'
import { setFlash } from 'sveltekit-flash-message/server'

  // 失敗後、メッセージを送信
  if (response.status !== 200) {
      setFlash({ message: 'ログインに失敗しました', type: 'error' }, event)
      return fail(400, { form })
    }
  }

トーストのカスタマイズは下記を参照
https://sonner.emilkowal.ski/