Netlify Build Plugins are the Swiss Army knife for customizing your Netlify build process, and their most surprising capability is that they can actually modify the build output itself, not just run scripts before or after.

Let’s see one in action. Imagine you have a site with a lot of Markdown files, and you want to inject a "Last updated" timestamp into each one. This isn’t something Netlify knows how to do out of the box.

Here’s a simplified example of a plugin that does just that.

// netlify-plugin-timestamp.js

module.exports = {
  async onBuild({ constants, utils }) {
    const fs = require('fs').promises;
    const path = require('path');

    const publicDir = constants.PUBLISH_DIR; // e.g., './dist'

    async function processFile(filePath) {
      try {
        let content = await fs.readFile(filePath, 'utf8');
        const stats = await fs.stat(filePath);
        const lastModified = stats.mtime.toISOString();

        // Inject the timestamp (example: append to the end)
        content += `\n\n<!-- Last updated: ${lastModified} -->`;

        await fs.writeFile(filePath, content, 'utf8');
        console.log(`Updated: ${path.relative(publicDir, filePath)}`);
      } catch (error) {
        console.error(`Error processing ${filePath}:`, error);
      }
    }

    async function walkDir(dir) {
      const entries = await fs.readdir(dir, { withFileTypes: true });
      for (const entry of entries) {
        const fullPath = path.join(dir, entry.name);
        if (entry.isDirectory()) {
          await walkDir(fullPath);
        } else if (entry.isFile() && path.extname(entry.name) === '.html') {
          await processFile(fullPath);
        }
      }
    }

    if (publicDir) {
      await walkDir(publicDir);
    }
  },
};

To use this, you’d save it as netlify-plugin-timestamp.js in your project’s root. Then, in your netlify.toml file, you’d add:

[[plugins]]
package = "./netlify-plugin-timestamp.js"

When Netlify builds your site, after your build command (e.g., npm run build) finishes and generates your static assets in the PUBLISH_DIR (usually ./public or ./dist), this plugin will run. It walks through all .html files in the PUBLISH_DIR, reads their content, gets the last modified timestamp of the source file, and appends it as an HTML comment.

The core problem Netlify Build Plugins solve is bridging the gap between your project’s build process and Netlify’s deployment environment. Your static site generator or framework does its thing, creating the final output. But what if you need to do something with that output before Netlify deploys it? Or what if you need to integrate with external services during the build? Plugins are the answer.

Internally, Netlify executes these plugins as Node.js modules. They hook into specific lifecycle events of the build process: onPreBuild, onBuild, onPostBuild, onEnd, and onError. The onBuild event, as shown above, is particularly powerful because it runs after your build command has completed but before Netlify bundles and deploys the site. This gives you access to the PUBLISH_DIR and the ability to modify its contents.

The constants object passed to the plugin provides crucial information like PUBLISH_DIR, SITE_ID, and NETLIFY_AUTH_TOKEN (if configured). The utils object offers helpful utilities like build.failBuild(), build.failPlugin(), and run.command().

The most counterintuitive aspect of build plugins is their ability to act as a true build step themselves, not just wrappers. Many developers think of them as simple pre- or post-build scripts. However, by directly manipulating files within the PUBLISH_DIR during the onBuild phase, a plugin can fundamentally alter what gets deployed. For instance, you could use a plugin to dynamically generate HTML files based on data fetched from an API, or to optimize images based on detected file types, effectively making the plugin a co-author of your build output alongside your main build command.

The next hurdle you’ll often encounter is managing plugin dependencies and ensuring they run in the correct order when you have multiple plugins.

Want structured learning?

Take the full Netlify course →