【Vite】SSRビルド時の静的ファイルコピー

更新日:2026/02/03

Viteはpublicフォルダにファイルを設置しておくと、ビルド時にそのファイルを出力フォルダにコピーしてくれる。
しかしpublicフォルダ以外のフォルダから出力フォルダにコピーしたいことがあります。

そんなときに定番なのが、vite-plugin-static-copy なんですが...
SSRビルドのときコピーしてくれません。
どうしたらいいの?というお話です。

 

方法1:environmentオプションを利用

vite-plugin-static-copyはSSRビルドでは利用できなかったのですが、vite-plugin-static-copy@3.2.0でenvironmentオプションが追加され、これを使うことでSSRビルドでも静的ファイルをコピーできるようになりました。

vite.config.js

import { viteStaticCopy } from "vite-plugin-static-copy";

export default {
  plugins: [
    viteStaticCopy({
      targets: [
        {
          src: "static/*",
          dest: "",
        },
      ],
      environment:"ssr"
    }),
  ],

environmentの初期値は"client"ですが、SSRビルドの時に何を指定するといいのかが不明でした。
そこで、node_modules/vite-plugin-static-copy/dist/index.js にconsole.log()を追加して実際の値を確認しました。
その結果"ssr"が指定されていたので、上記のようにオプションを記述したところ、想定した結果になりました。

※vite-plugin-static-copy@3.2.0は最近のリリース(記事作成時リリース後一週間以内)なので、今後のリリースでは内容が変更されるかもしれません。

 

方法2:writeBundleフックでNode.jsの組み込みモジュールを利用

vite.config.jsにNode.jsの組み込みモジュールを利用したファイルコピーコードを記述することもできます。
vite-plugin-static-copyの仕様変更で突然動作しなくなるかも?という人におススメ。

vite.config.js

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import fs from "fs";
import path from "path";

// https://vite.dev/config/
export default defineConfig(({  isSsrBuild }) => {

  return {
    build: isSsrBuild
      ? {
        ssr: true,
        outDir: "dist/server",
        rollupOptions: {
          input: "src/server/entry-server.jsx",
          output: {
            format: "esm",
          },
          plugins: [
            {
              name: "copy-static-files",
              writeBundle() {
                const files=["template.html","template.css"];
                files.forEach( f=>{
                  const src = path.resolve(`src/server/${f}`);
                  const dest = path.resolve(`dist/server/${f}`);
                  fs.copyFileSync(src, dest);
                });

              },
            },
          ],
        },
      }
      : {
        // CSR ビルド
        outDir: "dist/client",
      }
  }
});

writeBundleフックはViteのビルドフックで、バンドルファイルの出力完了後に呼び出されます。
ビルドフックにはgenerateBundleがありますが、これは出力前に呼び出されます。

今回は静的ファイルのコピー目的なのでどちらでもよさそうです。

 

方法3:ビルドに含めない

vite.config.jsでのビルド設定でコピーせずに、ビルド完了後にNode.js スクリプトで静的ファイルをコピーします。

次のコードは、前項のファイルコピーしているコードを抜き出したものです。

scripts/static-copy.js

import fs from "fs";
import path from "path";

const files=["template.html","template.css"];
files.forEach( f=>{
  const src = path.resolve(`src/server/${f}`);
  const dest = path.resolve(`dist/server/${f}`);
  fs.copyFileSync(src, dest);
});

次に、package.jsonでビルド後にスクリプトを呼び出します。

package.json

{
  "scripts": {
    "build:ssr": "vite build --ssr && node scripts/static-copy.js",
  }
}

なお、方法3のコードは記事作成中に方法2から抜き出しただけです。
動作確認していません。
動かなかったらごめんなさい。

更新日:2026/02/03

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。