Setting Up a Custom File Watcher in Astro for External Folders

3 min read, Mon, 17 Mar 2025

Astro is built on top of the Vite build tool and they come pre-packaged with watch files functionality during development. But setting up additional directories to watch which are outside of the project root is not state forward.

Many times there are scenarios when we want to watch files outside of the project root and anytime there is a change you want to restart the server to see the changes made. For example, in my case, I had a notes repository that was separate from the code repository, and any time I wrote a new note I wanted to see the real-time changes on the browser. Looking at the Vite’s server configuration, there is a server.watch configuration. I first assumed that it would be something that would help in setting up an additional directory to set up a file watcher.

Digging into Astro configuration but couldn’t find the configuration to add additional paths for the watcher.

Likely Astro has something called integrations and these integrations can have hooks. Hooks are like events that allow you to run some code when some hooks get fired in the Astro ecosystem. You can find more information on Astro’s integration API page.

Here are the steps to set up the watcher integration.

`astro.config.mjs` is the astros configuration file. You can find this file in the project root of Astro's project.

First, you need to create Astro’s inline integration (This is the easiest type of integration.). Open astro.config.mjs file and add the below code.

// https://astro.build/config
export default defineConfig({
    site: "<your site address>",
    integrations: [
        {
            name: "watcher-extension",
            hooks: {},
        },
    ],
});

Notice the integrations key inside defineConfig() method. This is the key where you will add our new integration. An inline integration is an object literal that contains the name of the integration and hooks that we want to listen to.

Next in the hooks object literal, we will add a key called astro:server:setup. astro:server:setup is a hook that we are interested in observing. This key accepts a method of the following type.

interface AstroIntegration {
    name: string;
    hooks: {
        // other hooks type declarations
        "astro:server:setup"?: (options: {
            server: vite.ViteDevServer;
            logger: AstroIntegrationLogger;
            toolbar: ReturnType<typeof getToolbarServerCommunicationHelpers>;
            refreshContent?: (options: RefreshContentOptions) => Promise<void>;
        }) => void | Promise<void>;
        // other hooks type declarations
    };
}

astro:server:setup accepts a function that accepts server, logger, toolbar, and a refreshContent() method. Detailed AstroIntegration interface can be found at Quick API Reference. We are interested in server parameter. Server parameter holds config key and a watcher object which is a chokidar object. This object supports event emitter add() which we will use to set up the watcher.

Now coming back to the astro.config.mjs. Let’s add the watcher code to it.

// https://astro.build/config
export default defineConfig({
    site: "<your site address>",
    integrations: [
        {
            name: "watcher-extension",
            hooks: {
                "astro:server:setup": ({ server }) => {
                    // resolve the path to the folder we want to watch.
                    // Replace <folder to watch> with actual path.
                    const notesPath = path.resolve("<folder to watch>");

                    // we mostly want to watch the files in development mode only.
                    // server.config.mode tells us which mode we are in.
                    if (server.config.mode !== "development") {
                        return;
                    }

                    // server.watcher.add() accept path which we want to watch.
                    server.watcher.add(notesPath);

                    // these are some misc event,
                    // we can log if things are going well as expected.
                    server.watcher
                        .on("add", (path) =>
                            console.log(`File ${path} has been added`)
                        )
                        .on("change", (path) =>
                            console.log(`File ${path} has been changed`)
                        );
                },
            },
        },
    ],
});

This is pretty much it. For the code explanation, please see inline comments in the code itself. Your watcher is ready.