コンテンツにスキップ

ルーティング

Astroはファイルベースルーティングを使用して、プロジェクトのsrc/pages/ディレクトリのファイルレイアウトを元にビルドURLを生成します。

Astroでは、ルート間の移動に標準的なHTMLの<a>要素を使用します。フレームワーク固有の<Link>コンポーネントは提供されていません。

src/pages/index.astro
<p>Astroの<a href="/about/">概要</a>についてもっと読む!</p>

src/pages/ディレクトリにある.astroページコンポーネント、MarkdownとMDXファイル(.md.mdx)は、自動的にウェブサイトのページとなります。各ページのルートは、src/pages/ディレクトリ内における自身のパスとファイル名に対応します。

# 静的ルーティングの例
src/pages/index.astro -> mysite.com/
src/pages/about.astro -> mysite.com/about
src/pages/about/index.astro -> mysite.com/about
src/pages/about/me.astro -> mysite.com/about/me
src/pages/posts/1.md -> mysite.com/posts/1

Astroページファイルのファイル名に動的ルートパラメータを指定すると、ファイルにマッチするページを複数生成できます。たとえばsrc/pages/authors/[author].astroは、ブログの各著者に対してプロフィールページを生成します。authorは、ページ内からアクセス可能なパラメーターとなります。

Astroのデフォルトの静的出力モードでは、これらのページはビルド時に生成されるため、authorと対応するファイルを取得する場合、それらのリストを事前に決めておく必要があります。SSRモードでは、ルートにマッチしたリクエストに応じてページが生成されます。

すべてのルートをビルド時に決める必要があるため、動的ルートはgetStaticPaths()をエクスポートし、そこでparamsプロパティをもつオブジェクトの配列を返す必要があります。各オブジェクトは対応するルートを生成します。

[dog].astroはファイル名に動的なdogパラメーターが定義されているため、getStaticPaths()から返されるオブジェクトのparamsにはdogを含める必要があります。Astro.paramsを使用してページからこのパラメーターにアクセスできます。

src/pages/dogs/[dog].astro
---
export function getStaticPaths() {
return [
{params: {dog: 'clifford'}},
{params: {dog: 'rover'}},
{params: {dog: 'spot'}},
];
}
const { dog } = Astro.params;
---
<div>いい子だ、{dog}</div>

上のコードにより、/dogs/clifford/dogs/rover/dogs/spotという3つのページが生成され、各ページでは対応する犬の名前が表示されます。

ファイル名には複数のパラメーターを含められますが、これらをすべてgetStaticPaths()paramsオブジェクトに含める必要があります。

src/pages/[lang]-[version]/info.astro
---
export function getStaticPaths () {
return [
{params: {lang: 'en', version: 'v1'}},
{params: {lang: 'fr', version: 'v2'}},
];
}
const { lang, version } = Astro.params;
---
...

上のコードは/en-v1/info/fr-v2/infoを生成します。

パラメーターはパス内の異なる部分に設定できます。たとえば、上と同じgetStaticPaths()をもつsrc/pages/[lang]/[version]/info.astroファイルは、/en/v1/info/fr/v2/infoのルートを生成します。

📚 getStaticPaths() (EN)についてもっと学ぶ。

関連レシピ: Add i18n features (EN)

より柔軟なURLルーティングが必要な場合は、.astroファイル名にレストパラメーター[...path])を使用することで、任意の深さのファイルパスにマッチさせられます。

src/pages/sequences/[...path].astro
---
export function getStaticPaths() {
return [
{params: {path: 'one/two/three'}},
{params: {path: 'four'}},
{params: {path: undefined }}
]
}
const { path } = Astro.params;
---
...

上のコードは/sequences/one/two/three/sequences/four/sequencesを生成します。(レストパラメーターをundefinedに設定することで、トップレベルのページにマッチさせられます。)

レストパラメーターは他の名前付きパラメーターと組み合わせて使用できます。たとえば、GitHubのファイルビューアは以下の動的ルートで表現できます。

/[org]/[repo]/tree/[branch]/[...file]

この例では、/withastro/astro/tree/main/docs/public/favicon.svgへのリクエストは、以下の名前付きパラメーターへと分割されます。

{
org: 'withastro',
repo: 'astro',
branch: 'main',
file: 'docs/public/favicon.svg'
}

以下の例は、レストパラメーター([...slug])とgetStaticPaths()props (EN)機能を使用して、異なる深さのスラグに対してページを生成します。

src/pages/[...slug].astro
---
export async function getStaticPaths() {
const pages = [
{
slug: undefined,
title: "Astroストア",
text: "Astroにようこそ!",
},
{
slug: "products",
title: "Astroグッズ",
text: "たくさんの商品があります",
},
{
slug: "products/astro-handbook",
title: "究極のAstroハンドブック",
text: "Astroについて学びたければ、この本を読む必要があります",
},
];
return pages.map(({ slug, title, text }) => {
return {
params: { slug },
props: { title, text },
};
});
}
const { title, text } = Astro.props;
---
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>{text}</p>
</body>
</html>

SSRモードでも、動的ルートは同じように定義されます。つまり、任意の文字列やパスにマッチさせるために、ファイル名に[param][...path]などのブラケットを含められます。しかし、各ルートは事前にビルドされないため、マッチしたすべてのルートに対してページが提供されます。これらは「静的」ルートではないため、getStaticPathsは使用できません。

src/pages/resources/[resource]/[id].astro
---
const { resource, id } = Astro.params;
---
<h1>{resource}: {id}</h1>

このページは、resources/users/1resources/colors/blueなど、任意のresourceidに対して提供されます。

SSRページではgetStaticPaths()を使用できないため、propsを受け取れません。しかし、オブジェクト内のslugパラメーターの値を参照することで、前の例をSSRモードにも対応させられます。”/“に対応するルートの場合、スラグパラメーターはundefinedになります。値がオブジェクトに存在しない場合は、404ページにリダイレクトします。

src/pages/[...slug].astro
---
const pages = [
{
slug: undefined,
title: "Astroストア",
text: "Astroにようこそ!",
},
{
slug: "products",
title: "Astroグッズ",
text: "たくさんの商品があります",
},
{
slug: "products/astro-handbook",
title: "究極のAstroハンドブック",
text: "Astroについて学びたければ、この本を読む必要があります",
},
];
const { slug } = Astro.params;
const page = pages.find((page) => page.slug === slug);
if (!page) return Astro.redirect("/404");
const { title, text } = page;
---
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>{text}</p>
</body>
</html>

読者を新しいページにリダイレクトしなければならない場合があります。サイトの構造が変更されたために永久的にリダイレクトする必要がある場合や、認証されたルートにログインするといったアクションに応じておこなう場合などです。

Astroの設定で、永久的に移動したページにユーザーをリダイレクトするルールを定義できます。また、ユーザーがサイトを利用した際に動的にリダイレクトすることもできます。

追加: astro@2.9.0

redirects値を使用して、Astroの設定から永久的なリダイレクトのマッピングを指定できます。ほとんどのリダイレクトでは、これは古いルートから新しいルートへのマッピングとなります。

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
redirects: {
'/old-page': '/new-page'
}
});

このリダイレクトは、ファイルベースルーティングと同じルールに従います。たとえば、新しいルートと古いルートの両方に同じパラメーターが含まれていれば、動的ルートも可能です。

{
"/blog/[...slug]": "/articles/[...slug]"
}

SSRまたは静的アダプターを使用するとオブジェクトを値として設定でき、そこでstatusコードや新しいdestinationを指定できます。

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
redirects: {
'/old-page': {
status: 302,
destination: '/new-page'
}
}
});

astro buildの実行時に、Astroはデフォルトでmeta refreshタグを含むHTMLファイルを出力します。サポートされているアダプターの場合は、代わりにホストの設定ファイルにリダイレクトを含めて出力します。

ステータスコードはデフォルトで301です。HTMLファイルをビルドする場合、ステータスコードはサーバーによって使用されません。

AstroグローバルのAstro.redirectメソッドを使用すると、別のページに動的にリダイレクトできます。たとえばクッキーからセッションを取得してユーザーのログイン状態を確認した後などにこれをおこないます。

src/pages/account.astro
---
import { isLoggedIn } from '../utils';
const cookie = Astro.request.headers.get('cookie');
// ユーザーがログインしていない場合は、ログインページにリダイレクトします
if (!isLoggedIn(cookie)) {
return Astro.redirect('/login');
}
---
<html>
<!-- ページの内容... -->
</html>

複数のルートが同じURLのパスにマッチすることがあります。たとえば、以下の各ルートは/posts/createにマッチします。

  • ディレクトリsrc/pages/
    • ディレクトリposts/
      • create.astro
      • [pid].astro
      • […slug].astro

Astroは、ページをビルドするためにどのルートを使用すべきかを知る必要があります。そのために、以下のルールにしたがってルートの順番を決定します。

  • パスパラメーターを持たない静的ルートは、他のすべてのルーティングよりも優先される
  • 名前付きパラメーターを使用する動的ルーティングは、レストパラメーターよりも優先される
  • 事前レンダリングされた動的ルートは、サーバーの動的ルートよりも優先される
  • レストパラメーターはもっとも低い優先度となる
  • エンドポイントは常にページよりも優先される
  • 同順位はアルファベット順に解決される

上記のようにファイルが配置されている場合に、リクエストされたURLと、HTMLをビルドするために使用されるルートがどのようにマッチングされるかの例をいくつか見てみましょう。

  • pages/posts/create.Astro - /posts/createをビルドします
  • pages/posts/[pid].astro - /posts/1, /posts/abcなどをビルドします。しかし、/posts/createはビルドされません
  • pages/posts/[...slug].Astro - /posts/1/2, /posts/a/b/cなどをビルドします。しかし、 /posts/create/posts/1/posts/abcはビルドされません

リダイレクトも同じルールに従いますが、優先順位は最後になります。つまり、ファイルベースのルートとリダイレクトが同じルート優先順位である場合、ファイルベースのルートが選択されます。

Astroは、複数のページに分割する必要がある大規模なデータコレクションのために、ページネーションを組み込みでサポートしています。Astroは、前ページと次ページのURL、総ページ数など、一般的なページネーションプロパティを生成します。

ページネーションされるルート名には、標準的な動的ルートと同じ[ブラケット]構文を使用する必要があります。たとえば、ファイル名 /astronauts/[page].astro/astronauts/1, /astronauts/2 などのルートを生成し、[page]は生成されるページ番号となります。

paginate()関数を使用すると、次のように値の配列に対してこれらのページを生成できます。

/src/pages/astronauts/[page].astro
---
export async function getStaticPaths({ paginate }) {
const astronautPages = [{
astronaut: 'ニール・アームストロング',
}, {
astronaut: 'バズ・オルドリン',
}, {
astronaut: 'サリー・ライド',
}, {
astronaut: 'ジョン・グレン',
}];
// 宇宙飛行士の配列から、1ページに2人づつ入るようにページを生成します
return paginate(astronautPages, { pageSize: 2 });
}
// ページネーションされたデータは、すべて"page"プロパティとして渡されます
const { page } = Astro.props;
---
<!--現在のページ番号を表示します。Astro.params.pageも使用可能です!-->
<h1>{page.currentPage}ページ</h1>
<ul>
<!--宇宙飛行士情報の配列を列挙します-->
{page.data.map(({ astronaut }) => <li>{astronaut}</li>)}
</ul>

これで、1ページに2つのアイテムが配置された、以下のページが生成されます。

  • /astronauts/1 - 1ページ目には「ニール・アームストロング」と「バズ・オルドリン」を表示します
  • /astronauts/2 - 2ページ目には「サリー・ライド」と「ジョン・グレン」を表示します

paginate()関数を使用すると、各ページのデータはpageプロパティで渡されます。pageプロパティは多くの便利なプロパティを持っていますが、ここではそのうち重要なものを紹介します。

  • page.data - paginate()関数に渡された、ページのスライスデータを含む配列です
  • page.url.next - セット内の次のページへのリンクです
  • page.url.prev - セット内の前のページへのリンクです
/src/pages/astronauts/[page].astro
---
// 前の例と同じように、{ astronaut } オブジェクトのリストをページネーションします
export async function getStaticPaths({ paginate }) { /* ... */ }
const { page } = Astro.props;
---
<h1>{page.currentPage}ページ</h1>
<ul>
{page.data.map(({ astronaut }) => <li>{astronaut}</li>)}
</ul>
{page.url.prev ? <a href={page.url.prev}>Previous</a> : null}
{page.url.next ? <a href={page.url.next}>Next</a> : null}
interface Page<T = any> {
/** 結果 */
data: T[];
/** メタデータ */
/** 0から始まる、ページ上の最初のアイテムのインデックス */
start: number;
/** 0から始まる、ページ上の最後のアイテムのインデックス */
end: number;
/** 結果の総数 */
total: number;
/** 1から始まる、現在のページ番号 */
currentPage: number;
/** 1ページあたりのアイテム数(デフォルトは25) */
size: number;
/** 最終ページ番号 */
lastPage: number;
url: {
/** 現在のページのURL */
current: string;
/** 前のページのURL(もしあれば) */
prev: string | undefined;
/** 次のページのURL(もしあれば) */
next: string | undefined;
};
}

ページネーションのより高度なユースケースはネストされたページネーションです。これは、ページネーションを他の動的ルーティングパラメーターと組み合わせたものです。ネストされたページネーションを使用すると、ページネーションされたコレクションを何らかのプロパティやタグでグループ化できます。

たとえば、ページネーションされたMarkdownの投稿を何らかのタグでグループ化したい場合、以下のURLにマッチする/src/pages/[tag]/[page].Astroページを作成してネストされたページネーションを使用します。

  • /red/1 (tag=red)
  • /red/2 (tag=red)
  • /blue/1 (tag=blue)
  • /green/1 (tag=green)

ネストされたページネーションは、paginate()の結果をグループごとに配列としてgetStaticPaths()から返すことで動作します。

以下の例では、上記のURLを作成するために、ネストされたページネーションを実装しています。

/src/pages/[tag]/[page].astro
---
export function getStaticPaths({paginate}) {
const allTags = ['red', 'blue', 'green'];
const allPosts = await Astro.glob('../../posts/*.md');
// すべてのタグに対して、paginate()の結果を返します。
// その結果がどのタググループに対するものかAstroに伝えるために、
// `{params: {tag}}`を必ず`paginate()`に渡してください。
return allTags.map((tag) => {
const filteredPosts = allPosts.filter((post) => post.frontmatter.tag === tag);
return paginate(filteredPosts, {
params: { tag },
pageSize: 10
});
});
}
const { page } = Astro.props;
const params = Astro.params;

アンダースコア(_)を接頭辞としてファイル名に付けることで、ページやディレクトリをビルドから除外できます。アンダースコアで始まるファイルはルーターによって認識されず、dist/ディレクトリにも配置されません。

これを使用すると、一時的にページを無効にしたり、テストやユーティリティ、コンポーネントを関連するページと同じフォルダーに配置したりできます。

以下の例では、src/pages/index.astrosrc/pages/posts/post1.mdのみがページルートとHTMLファイルとしてビルドされます。

  • ディレクトリsrc/pages/
    • ディレクトリ_hidden-directory/
      • page1.md
      • page2.md
    • _hidden-page.astro
    • index.astro
    • ディレクトリposts/
      • _SomeComponent.astro
      • _utils.js
      • post1.md