Remix v2 でクライアントサイドに環境変数を渡す方法
こんにちは。Gaji-Laboの村上です。
使われることが増えてきた Remix v2 で、クライアントサイドに環境変数を渡す方法について紹介します。
通常、環境変数はサーバーサイドでのみ使用されるべきですが、特定のケースではクライアントサイドで使用したい場合もあります。
ただし、クライアントサイドに渡す情報は第三者に見られる可能性があるため、非常に慎重に取り扱う必要があります。
APIキーやデータベースの接続情報などの機密情報は、絶対にクライアントサイドに渡さないでください。
1. window オブジェクト と dangerouslySetInnerHTML を使う
まず最初に、公式ドキュメントでも推奨されている window オブジェクト
と dangerouslySetInnerHTML
を使う方法を紹介します。
これは、ルートの loader
関数から環境変数を返し、インラインスクリプトを使用してクライアントサイドでグローバルに利用可能にする方法です。
1-1. .env ファイルの例
以下のような環境変数が .env
ファイルに定義されていると仮定します。
HOGE_KEY="xxxxxxxhoge"
FUGA_KEY="xxxxxxxfuga"
1-2. ルートの loader からクライアントサイドに 環境変数を ENV として渡す
次に、loader
関数を使用して、これらの環境変数を ENV として渡します。
export async function loader() {
return json({
ENV: {
HOGE_KEY: process.env.HOGE_KEY,
FUGA_KEY: process.env.FUGA_KEY,
},
});
}
1-3. ENV を window オブジェクトに設定
クライアントサイドで useLoaderData
と dangerouslySetInnerHTML
を使用して、ENV
を window オブジェクト
に設定します。
export async function loader() {
return json({
ENV: {
HOGE_KEY: process.env.HOGE_KEY,
FUGA_KEY: process.env.FUGA_KEY,
},
});
}
export function Root() {
const data = useLoaderData<typeof loader>();
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<Outlet />
<script
dangerouslySetInnerHTML={{
__html: `window.ENV = ${JSON.stringify(
data.ENV
)}`,
}}
/>
<Scripts />
</body>
</html>
);
}
1-4. window.ENV を確認する
ルート以外のページで window.ENV を確認してみましょう。
クライアントサイドでしかアクセスできないため、useEffect
フックや if (typeof document !== "undefined")
を使用して window.ENV の値が利用可能なタイミングでアクセスします。
export default function Foo() {
if (typeof document !== "undefined") {
console.log(window.ENV)
}
return (
<div>
<h1>foo</h1>
</div>
)
}
ページを表示すると、HOGE_KEY と FUGA_KEY がコンソールに表示されます。
1-5. window.ENV の型エラーを解消する
window.ENV を使用すると、TypeScript で次のような型エラーが発生します。プロパティ 'ENV' は型 'Window & typeof globalThis' に存在しません。
これを解消するために、次のように型定義を行います。
interface Window {
ENV: {
HOGE_KEY: string;
FUGA_KEY: string;
};
}
これにより、TypeScriptでの型エラーが解消されます。
参考
https://remix.run/docs/en/main/guides/envvars
https://remix.run/docs/en/main/guides/constraints
2. useRouteLoaderDataを使う
2つ目の方法は、公式の GitHub の Discussions で提案されている、useRouteLoaderData
フックを使う方法です。
window オブジェクトを使うのではなく、loader
から返されるデータにアクセスするため、クライアントサイドの安全性を少し向上させたい場合に役立ちます。
特に、dangerouslySetInnerHTML
を避けたい場合におすすめです。
2-1. ルート の loader からクライアントサイドに ENV を返す
まず、.env
ファイルに環境変数を設定し、loader
関数で環境変数を返します。
この部分は、「1. window.ENV と dangerouslySetInnerHTML を使う方法」と同じです。
HOGE_KEY="xxxxxxxhoge"
FUGA_KEY="xxxxxxxfuga"
export async function loader() {
return json({
ENV: {
HOGE_KEY: process.env.HOGE_KEY,
FUGA_KEY: process.env.FUGA_KEY,
},
});
}
2-2. useRouteLoaderData で ENV を利用する
ルート以外のページでも、useRouteLoaderData を使って ルート の loader から渡された環境変数にアクセスできます。
これにより、各ページで直接 ENV にアクセスできるため、環境変数を使いやすくなります。
import { useRouteLoaderData } from "@remix-run/react";
import type { loader as rootLoader } from "~/root";
export default function Foo() {
const rootData = useRouteLoaderData<typeof rootLoader>("root")!;
console.log(rootData.ENV);
return (
<div>
<h1>foo</h1>
</div>
)
}
参考
https://github.com/remix-run/remix/discussions/8704#discussioncomment-8397380
3. Vite を使っている場合
Remix v2 は、デフォルトでビルドツールとして Vite を採用しています。
そのため、Vite を使っている場合は、環境変数の名前に VITE_
という接頭辞をつけることで、import.meta.env
を使って環境変数にアクセスできます。
3-1. .env ファイルの設定
Vite ではクライアントサイドに渡したい環境変数に、接頭辞 VITE_
を設定します。
VITE_HOGE_KEY="xxxxxxxhoge"
3-2. クライアントサイドでの環境変数の使用
export default function Foo() {
console.log(import.meta.env.VITE_HOGE_KEY);
return (
<div>
<h1>foo</h1>
</div>
);
}
これにより、環境変数 VITE_HOGE_KEY
の値がコンソールに表示されます。
参考
https://ja.vitejs.dev/guide/env-and-mode
4. root の ErrorBoundary で環境変数を使いたい場合
ErrorBoundary 内では、useLoaderData
や useRouteLoaderData
を使って、環境変数にアクセスすることはできません。
これは、useLoaderData
がErrorBoundary内で動作しないことや、useRouteLoaderData
が undefined
を返す場合があるためです。
それでも、root
の ErrorBoundary
内で環境変数を使いたい場合には、公式の GitHub の Discussions で提案されているプロジェクトが参考になります。
https://github.com/kiliman/remix-global-data
具体的なユースケースとして、エラーハンドリングが必要な場合や、エラーページでも特定の環境変数を参照する必要がある場合に、このアプローチが役立ちます。
参考
https://github.com/remix-run/remix/discussions/9043
https://github.com/kiliman/remix-global-data
最後に
今回紹介した複数の方法を活用することで、状況に応じて最適な環境変数の管理・使用が可能になります。
Gaji-Labo はスタートアップに頭数や技術だけを提供するのではなく、チームの一員として成長を担うことを価値としています。
その価値を出せるよう(セキュリティを配慮するのは当然として)、この記事に書いたような状況に応じた判断や最適な実装ができる引き出しを増やしていきたいと思います。
そんなふうに専門技術を軸にさまざまなスタートアップへチームワークを提供したいエンジニアやデザイナーの方はとてもGaji-Labo に向いていると思います。
カジュアル面談でお話ししませんか? いますぐ転職を考えていない方でも歓迎です!
Gaji-Labo フロントエンドエンジニア向けご案内資料
今すぐの転職でなくてもOKです!まずはお話しませんか?
現在弊社では一緒にお仕事をしてくださるエンジニアさんやデザイナーさんを積極募集しています。まずはカジュアルな面談で、お互いに大事にしていることをお話できたらうれしいです。詳しい応募要項は以下からチェックしてください。
- React, Next.js を得意とするフロントエンドエンジニア募集要項
- シニアクラスのフロントエンドエンジニア募集要項
- 抽象的な物事を具体的な機能にビジュアライズできるUIデザイナー募集要項
- UIデザイナーとして手ざわりのいいUIを作りたい業務委託パートナーさん募集(Wantedly)
パートナー契約へのお問い合わせもお仕事へのお問い合わせも、どちらもいつでも大歓迎です。まずはオンラインでの面談でお話しましょう。ぜひお気軽にお問い合わせください!
話をしてみたい!