> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overlayed.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuring App & Site Bundles

> Tune the include and exclude globs in overlayed.config.ts so your app and site bundles ship exactly the right files.

When it's time to deploy, `overlayed bundle` zips your project into two uploads: an **app bundle** (your Electron code)
and a **site bundle** (your frontend). What lands in each zip is decided entirely by the `app` and `site` globs in your
`overlayed.config.ts`. This guide walks through getting those globs right - pulling in the runtime dependencies your app
needs, trimming out the files it doesn't, and keeping the config working across development and CI.

<Info>
  This is the workflow companion to the [OverlayedConfig reference](/packages/overlayed-config) (every field and its
  type) and [Understanding Bundles](/deployment/introduction) (how bundles become releases). Read those for the full
  API surface.
</Info>

## How Bundling Reads Your Config

Three facts shape every decision below:

1. **There is no `npm install` on the server.** The build only sees the files you bundle. If a runtime dependency isn't
   matched by an `include` glob, it won't exist in production - this is the single most common cause of a working dev
   build that crashes once released.
2. **`app` and `site` are bundled independently.** Each has its own `include`, `exclude`, and `baseDir`. You can bundle
   just one at a time, and they can point at completely different directories.
3. **Globs are resolved relative to the config file** - or to `baseDir` when you set it. `package.json` and
   `node_modules/@overlayed/app` are always added to the app bundle, and `overlayed.config.ts` is always excluded from
   both bundles (the app bundle also excludes any `installer` folder; the site bundle does not).

<Info>
  **Why bundling works this way.** Overlayed deliberately does not build your project for you - we never run
  `npm install`, and we do not build your `site`. You compile your Electron code and your frontend however you like,
  with whatever tooling and dependencies you choose, and the bundle ships exactly those files. That keeps your build
  pipeline fully in your control. The one thing we *do* build for you is the Electron app: your app bundle is turned
  into a signed installer for your users.
</Info>

## Configuring the Two Bundles

<Steps>
  <Step title="Start from a distribution-ready config">
    A complete config loads the `applicationId` from the environment and describes both bundles. This is the shape
    to work from:

    ```ts overlayed.config.ts theme={null}
    import { defineConfig } from "@overlayed/app";
    import { loadEnv } from "vite";

    const env = loadEnv(process.env.NODE_ENV ?? "development", process.cwd(), "VITE_");

    export default defineConfig({
    	applicationId: env.VITE_APPLICATION_ID,
    	app: {
    		include: ["out/**/*", "node_modules/@electron-toolkit/**"],
    	},
    	site: {
    		baseDir: "./out/renderer",
    		include: ["**/*"],
    	},
    });
    ```

    Using `loadEnv` keeps the ID out of source control and lets the same file resolve a different application in CI
    versus locally. `applicationId` must be a valid ULID - `defineConfig` throws immediately if it isn't.

    <Info>
      The site bundle is only consumed at deploy time; during development your frontend runs from its own dev
      server. You can leave the `site` config in place year-round - it does nothing until you bundle.
    </Info>
  </Step>

  <Step title="Include your app's runtime dependencies">
    The app bundle needs your compiled output **and** every `node_modules` package your Electron process loads at
    runtime. List each one explicitly so unused dependencies don't bloat the zip:

    ```ts overlayed.config.ts theme={null}
    app: {
    	include: [
    		"out/**/*", // your compiled Electron code
    		"node_modules/@electron-toolkit/**", // a runtime dependency
    	],
    },
    ```

    <Tip>
      You never need to list `package.json` or `node_modules/@overlayed/app` - both are always added to the app
      bundle for you.
    </Tip>

    <Warning>
      Native modules and anything your `main` process `require`s or `import`s at runtime must be included. A
      missing dependency won't fail the bundle - it fails at launch in production with a module-not-found error.
    </Warning>
  </Step>

  <Step title="Point the site bundle at your build output">
    Set `baseDir` to your frontend's build directory so the pattern stays a simple `**/*` instead of repeating the
    prefix. Everything under `baseDir` is resolved relative to it:

    ```ts overlayed.config.ts theme={null}
    site: {
    	baseDir: "./out/renderer",
    	include: ["**/*"],
    },
    ```

    A site bundle must contain at least one `.html` file - if the glob matches no HTML, `overlayed bundle` fails
    with `Site bundle must contain at least one HTML file`.
  </Step>

  <Step title="Bundle and verify">
    Run the CLI. With no flags it prompts for which bundles to build; the flags below target one directly:

    <CodeGroup>
      ```bash Both theme={null}
      overlayed bundle
      ```

      ```bash App only theme={null}
      overlayed bundle --app
      ```

      ```bash Site only theme={null}
      overlayed bundle --site
      ```
    </CodeGroup>

    Before uploading anything, inspect the zip locally with the `--debug` flag. In debug mode the CLI writes
    `.overlayed/tmp/debug-app.zip` and `.overlayed/tmp/debug-site.zip` **instead of** uploading, and logs each file
    it adds:

    ```bash theme={null}
    overlayed bundle --debug
    ```

    Open the zip and confirm your compiled code, dependencies, and `package.json` are all present and nothing secret
    or oversized slipped in.
  </Step>
</Steps>

## Excluding Files

When a broad `include` is easier than enumerating everything, cast a wide net and trim it with `exclude`. It accepts a
single glob or an array, resolved from the same `baseDir` as `include`:

```ts overlayed.config.ts theme={null}
app: {
	include: ["out/**/*", "node_modules/**"],
	exclude: [
		"**/*.map", // source maps
		"**/*.md", // dependency readmes
		"**/test/**", // dependency test folders
		"**/*.d.ts", // type declarations
		"node_modules/.bin/**",
	],
},
```

This keeps the whole dependency tree while dropping the parts that never run in production. `exclude` works identically
on the `site` bundle - for example, dropping `**/*.map` from a frontend build.

<Tip>
  `overlayed.config.ts` is removed from every bundle automatically, and the **app** bundle also drops any `installer`
  folder for you. The site bundle does **not** - if a broad site `include` could match an `installer` directory, add
  an explicit `exclude` for it.
</Tip>

## Reserved Files

A few names are reserved and will fail the app bundle if your globs pick them up:

* An `install` **folder** at the bundle root (reserved for the installer).
* The files `meta.json` and `app-update.yml` at the bundle root (reserved for internal use).

If you hit one of these, tighten the offending `include` pattern or add an `exclude` entry for it.

## FAQ

<AccordionGroup>
  <Accordion title="Why does my app work in development but crash after release?">
    Almost always a missing runtime dependency. The build server never runs `npm install` - only bundled files
    exist in production. Add every `node_modules` package your `main` process loads to the app `include`, then run
    `overlayed bundle --debug` and confirm it's in the zip.
  </Accordion>

  <Accordion title="What's the difference between baseDir and prefixing my globs?">
    They're equivalent for matching, but `baseDir` also sets where relative paths in the zip start. `baseDir:
        		"./out/renderer"` with `include: ["**/*"]` bundles the *contents* of `out/renderer` at the zip root - ideal for a
    site whose `index.html` must sit at the top level.
  </Accordion>

  <Accordion title="Do I need a site config during development?">
    No. The site bundle is only used when you deploy - your frontend runs from its own dev server locally. You still
    declare `site` in the config so `overlayed bundle` can build it when you ship.
  </Accordion>

  <Accordion title="Can I bundle from a monorepo where dependencies are hoisted?">
    Yes. Version resolution for `@overlayed/app` and `@overlayed/electron` defaults to `./node_modules` relative to
    the app `baseDir`; point it elsewhere with `nodeModulesDir`, or take full control with `resolvePackageVersion`.
    See the [reference](/packages/overlayed-config#app-bundle-config).
  </Accordion>

  <Accordion title="How do I keep the applicationId out of source control?">
    Load it from the environment with `loadEnv` (shown above) and store the value in a `.env` file you gitignore, or
    in your CI secrets. The ID must still resolve to a valid ULID at bundle time.
  </Accordion>

  <Accordion title="Can I bundle without a git repository or CI commit?">
    Yes. The commit hash defaults to `git rev-parse HEAD`; override it - or omit it entirely by returning
    `undefined` - via `resolveCommitHash` in the config.
  </Accordion>

  <Accordion title="My site bundle fails with 'must contain at least one HTML file'.">
    Your `include` matched no `.html`. Check that `baseDir` points at the built output (not your source directory)
    and that the build ran before bundling.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="OverlayedConfig Reference" icon="gear" href="/packages/overlayed-config">
    Every config field and its exact type.
  </Card>

  <Card title="Understanding Bundles" icon="box" href="/deployment/introduction">
    How bundles become releases and reach your users.
  </Card>

  <Card title="Overlayed Config" icon="file-code" href="/essentials/overlayed-config">
    The config file's role in the overall project.
  </Card>

  <Card title="Site Updates" icon="rotate" href="/deployment/updates">
    Ship frontend changes without a new app release.
  </Card>
</CardGroup>
