Murasaki
ガイド

ルーティング

src/app 上のファイルベースルーティング — ページ、レイアウト、動的セグメント、ナビゲーション。

src/app/**/page.tsx はそれぞれ自動的にルートになります — ルーター設定を書く必要はありません。Vite プラグインが src/app をスキャンして仮想のルートテーブルを生成し、(スキャフォールドのエントリによってマウントされる)<AppRouter> がナビゲーションのたびにそれとマッチングします。

src/app/
├─ layout.tsx        # すべてのルートをラップする
├─ page.tsx          # "/"
├─ about/
│  └─ page.tsx       # "/about"
└─ blog/
   └─ [slug]/
      └─ page.tsx    # "/blog/:slug"

レイアウト

layout.tsx はその配下のすべてのルートをラップします(ネストしたレイアウトはルートからリーフへと合成されます)。ルートレイアウトは、murasaki が提供するフルウィンドウのフレームコンポーネントである <App> で子要素をラップする必要があります:

src/app/layout.tsx
import type { ReactNode } from 'react'
import { App, useContextMenu } from 'murasaki'

export default function Layout({ children }: { children: ReactNode }) {
  useContextMenu([{ label: 'Reload', shortcut: 'command,R', action: () => location.reload() }])

  return <App className="flex items-center justify-center">{children}</App>
}

動的セグメント

[param] フォルダは URL のセグメントをキャプチャします。useParams() で読み取れます:

src/app/blog/[slug]/page.tsx
import { useParams } from 'murasaki'

export default function BlogPost() {
  const { slug } = useParams()
  return <p>Post: {slug}</p>
}

静的セグメントは常に動的セグメントより優先され、より具体的なマッチ(リテラルセグメントが多い方)がより非具体的なものより優先されます — Next.js と同じ優先順位です。(group) フォルダ(括弧を使い、ブラケットは使わない)は、URL セグメントを追加せずにルートを整理します。

キャッチオールセグメント

[...name] フォルダは残りのセグメントすべてを string[] としてキャプチャします。少なくとも 1 つのセグメントがマッチする必要があります。[[...name]](二重ブラケット)はオプショナルな形式で、親パス自体にもマッチし、その場合パラメータは undefined のままになります:

src/app/docs/[[...slug]]/page.tsx
import { useParams } from 'murasaki'

export default function DocsPage() {
  const { slug } = useParams()
  // "/docs"      -> slug は undefined
  // "/docs/a/b"  -> slug は ["a", "b"]
  return <p>{slug ? slug.join('/') : 'index'}</p>
}

優先順位は Next.js と同じです: 静的 > 動的([name]) > キャッチオール([...name]) > オプショナルキャッチオール([[...name]])。

loading / error / not-found バウンダリ

これらのファイルを page.tsx の隣(またはその上位)に置きます:

  • loading.tsx — ページがサスペンドしている間に表示されます(<Suspense> でラップされます)。
  • error.tsx — エラーバウンダリです。{ error, reset } を受け取るコンポーネントをエクスポートします。そのルート配下で発生したレンダーエラーをキャッチします。
  • not-found.tsx — マッチするルートがない場合にレンダーされます。URL の祖先チェーンの中で最も近いものが優先されます。

それぞれが Next.js の App Router と同様に、自身のサブツリーにスコープされます。

ナビゲーション

クライアントサイドナビゲーション(フルリロードなし)には <Link> を使います:

import { Link } from 'murasaki'

<Link href="/about">About</Link>

useRouter()push / replace / back / pathname を命令的に提供し、usePathname() は現在のパスを読み取ります。push / replace / <Link href> に付けたクエリ文字列(例: /search?q=cats)は自動的にパスから分離されます — ルートマッチングはパスだけを見るため、クエリだけのナビゲーションはmiddlewareを再実行せず、レンダーされるページも変わりません。

useSearchParams() は現在のクエリ文字列を URLSearchParams として読み取り、すべてのナビゲーションに反応します:

import { useSearchParams } from 'murasaki'

function SearchResults() {
  const params = useSearchParams()
  return <p>Query: {params.get('q')}</p>
}

オリジンが異なる URL への通常の <a href="https://..."> は、アプリのウィンドウ内ではなく、ユーザーのデフォルトのシステムブラウザで開きます — 外部リンクを特別扱いする必要はありません。

次へ

GitHub でこのページを改善

On this page