📦 I Built 3 Astro Integrations and Shipped Them to npm — Here’s What They Do

I’ve been building with Astro for a while now, and the same pain points kept showing up across projects. I got tired of patching around them and just built integrations instead.

Three packages. All on npm. All open source.

let's begin

🎯 What’s an Astro Integration, anyway?

If you’ve never built one — an Astro Integration is a package that hooks into Astro’s build lifecycle or dev toolbar. You return a name and a hooks object, and Astro calls your callbacks at the right moments.

export default function myIntegration(): AstroIntegration {
  return {
    name: 'my-integration',
    hooks: {
      'astro:build:done': ({ pages }) => {
        // runs after build
      }
    }
  }
}

That’s basically it. It’s how @astrojs/react, Tailwind, Partytown, and most of the ecosystem slot into your config without you wiring anything up yourself.

📦 @shiftescape/astro-bundle-budget

You build your site, ship it, and two weeks later someone notices a page is pulling in 400KB of JS. No warnings. No CI failure. Just a slow page and a mild sense of shame.

This integration hooks into astro:build:done, reads the output, and compares your JS/CSS sizes against budgets you set. Exceed them — build fails.

import bundleBudget from '@shiftescape/astro-bundle-budget';

export default defineConfig({
  integrations: [
    bundleBudget({
      budgets: [
        { type: 'js', budget: 100 },  // 100KB JS max
        { type: 'css', budget: 50 }   // 50KB CSS max
      ]
    })
  ]
});

Set it once, forget about it. The pipeline tells you when something bloats.

programmer suprised

📥 npm · source

🔍 @shiftescape/astro-env-inspector

You’ve got env vars spread across .env, .env.local, .env.production. You’re debugging something weird and you genuinely don’t know which value Astro is actually reading — or if the variable is even loaded at all.

This adds a panel to the Astro Dev Toolbar. All your env vars, grouped, masked by default, searchable. Dev only — nothing touches your production build.

import envInspector from '@shiftescape/astro-env-inspector';

export default defineConfig({
  integrations: [envInspector()]
});

Beats staring at console.log(import.meta.env) output and trying to remember which file overrides which.

inspector

📥 npm · source

🗺️ @shiftescape/astro-toolbar-routes

Once a project gets past a dozen pages, you’re either memorizing routes or digging through src/pages/ every time. Neither is fun.

This puts a route map in the dev toolbar — every page in your project, grouped, searchable, clickable. It reads your file-based routes at build time and lists them out.

import toolbarRoutes from '@shiftescape/astro-toolbar-routes';

export default defineConfig({
  integrations: [toolbarRoutes()]
});

Click a route, go there. That’s the whole thing.

explorer

📥 npm · source

Using all three together

npm install @shiftescape/astro-bundle-budget @shiftescape/astro-env-inspector @shiftescape/astro-toolbar-routes
// astro.config.mjs
import { defineConfig } from 'astro/config';
import bundleBudget from '@shiftescape/astro-bundle-budget';
import envInspector from '@shiftescape/astro-env-inspector';
import toolbarRoutes from '@shiftescape/astro-toolbar-routes';

export default defineConfig({
  integrations: [
    bundleBudget({ budgets: [{ type: 'js', budget: 100 }] }),
    envInspector(),
    toolbarRoutes()
  ]
});

The toolbar panels are dev-only. The budget check runs at build time and exits non-zero on failure, so CI picks it up without any extra config.

That’s it for now

All three are at v0.1.x — functional and tested, but still early. There’s a lot more I want to do with each of them

If any of these save you some time, a ⭐ on GitHub goes a long way!

bye-son

Built with the Astro Integration API and listed in the Astro Integrations Library.