はじめに
Jamstack構成でSvelteKit/SSG(adapter-static)を採用しWebサイトを構築する際に、カスタマイズされた404 Not Foundページを表示したい場合には、ホスティングサービスにあわせた404 Not Foundページを出力するように構築する必要がある。
例えばCloudflare Pagesの場合には、リクエストされたページが存在しない場合には、指定されたパスと同一ディレクトリ上の404.html
を探し、なければディレクトリツリーを検索し続けることで最終的には/404.html
をレンダリングする仕様になっている。
この404.html
のスマートな出力方法が意外なことに見当たらなかったため、独自の実装方法で対応した。
注意
本記事の実装例はSvelteKit及びadapter-staticの公式ドキュメントに記載されている方法ではありません。
概要
SvelteKitでは、+error.svelte
を実装することで、各routeごとにエラーページをカスタマイズすることができる。
細かなエラー画面の出し分けなどは一旦置いておくとして、存在しないページへのリクエストのみ共通の404 Not Foundページを表示したいという場合には/routes/+error.svelte
を設置するだけで共通の404 Not Foundページが出力できる。
しかし、SSG(Static Site Generator)においては、カスタマイズされた404 Not Foundページを表示したい場合に、ホスティングサービスにあわせたエラー用の静的なHTMLを設置する必要があり、冒頭で例に挙げたCloudflare Pagesの場合には404.html
というファイルが必要になる。
ここまでを踏まえて、/routes/+error.svelte
をビルドディレクトリに任意のファイル名(今回の例では404.html
)で出力する方法が公式に提供されているのでは?と思い公式ドキュメント(主にadapter-staticに関する情報)を見たが、はっきりとマッチする情報が見つからない。。
ということでSvelteKitのGitHubリポジトリのIssueを探してみると、まさに全く同じことを実現する方法について、質問が出ていた。
このIssueは結局こちらのIssueに引き継がれることとなったのだが、最終的にコントリビューターの方のコメントとともに既に適切な機能提供がされているという理由でクローズされている。
しかし、実際に提示されている方法を試してみたところ当初の質問に対する回答、機能提供としては少し不足がある気がする。
というのも最終的にadapter-static
のfallback
オプションで出力したいエラーページ(404.html)を指定し、prerender
をtrue
にするという方法が提示されているのだが、このコメントの通りこの方法は/routes/+error.svelte
を404.htmlとして出力するわけではなく、404.htmlを空ページで出力しているだけに見受けられる。
また、ConfigurationのerrorTemplate
など色々と試してみたが、adapter-static
のfallback
に指定したページをカスタマイズする方法がどこにも見当たらない。
※ここは私の調査が不足している可能性もあるのでご存じの方がいましたらコメントいただければと
ということで一旦、公式に提供されている機能での実現を諦め、Issueで他の方がコメントしているやり方をベースに独自の実装方法で対応することとした。
対応
まず、シンプルにビルドディレクトリに/404.html
が出力されれば良いだけなので、直接/routes/404/+page.svelte
を実装すれば解決では?と考えた。
注意
trailingSlash
がalways
だと、/404.html
ではなく、/404/index.html
が出力されてしまうが、Cloudflare PagesではRoute matchingがこのような仕様のためtrailingSlash
がnever
という前提のもとの対応。
実際、この方法でビルドしてみると/routes/404/+page.svelte
の内容が/404.html
として出力できた。
また、$ vite dev
で稼働する開発環境では引き続き/routes/+error.svelte
が404 Not Foundページになるが、別ファイルで同じレイアウトを管理するのは冗長なので、/routes/404/+page.svelte
で/routes/+error.svelte
をレンダリングするような実装としておくことで一つのページで管理することができる。
<script lang="ts">
import ErrorPage from '$routes/+error.svelte'
</script>
<ErrorPage />
まとめ
今回の実装例は公式ドキュメントで提示されている方法ではなく少しハック感が強いため、今だに「適切な方法が提供されているのでは?」と思っているのでご存じの方がいましたらコメントいただけますと幸いです。
参考