# Best Practices for Organizing Large Gatsby Projects with Multiple Plugins and Data Sources

> Master organizing large Gatsby projects. Discover best practices for plugins, data sources, themes, and performance to build scalable sites efficiently. Learn more today.

- Repository: [Gatsby/gatsby](https://github.com/gatsbyjs/gatsby)
- Tags: best-practices
- Published: 2026-03-06

---

**Organize large Gatsby projects by maintaining the canonical `/src` and `/plugins` directory structure, grouping plugins logically in [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js), leveraging themes for reusable configurations, and enabling performance features like parallel sourcing and stateful source nodes.**

Managing a growing Gatsby codebase requires discipline as you integrate multiple CMSs, APIs, and databases. According to the `gatsbyjs/gatsby` source code and official documentation, strategic organization of your directory layout and plugin architecture prevents build slowdowns and reduces cognitive overhead for developers.

## Maintain the Canonical Project Structure

Gatsby enforces a conventional file structure that separates auto-generated artifacts from source code. As documented in [`docs/docs/reference/gatsby-project-structure.md`](https://github.com/gatsbyjs/gatsby/blob/main/docs/docs/reference/gatsby-project-structure.md), you should treat `/.cache` and `/public` as build artifacts—both directories must be added to `.gitignore` and left untouched.

Place all application source code under `/src`, including components, pages, and templates. Reserve the top-level `/plugins` directory exclusively for **local plugins** (custom plugins that are specific to your project and version-controlled alongside your site). This separation ensures that your core source tree remains clean while reusable data-sourcing logic lives in isolated modules.

## Group Plugins Logically in [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js)

The [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js) file serves as the central registry for all plugins. According to the Config API documentation in [`docs/docs/reference/config-files/gatsby-config.md`](https://github.com/gatsbyjs/gatsby/blob/main/docs/docs/reference/config-files/gatsby-config.md), the order of the `plugins` array matters: Gatsby runs plugins in the sequence they are defined.

Structure your configuration by categorizing plugins into distinct groups:

1.  **Source plugins** (e.g., `gatsby-source-wordpress`, `gatsby-source-contentful`) that fetch external data
2.  **Transformer plugins** (e.g., `gatsby-transformer-sharp`) that process raw data into Gatsby nodes
3.  **UI and SEO plugins** (e.g., `gatsby-plugin-image`, `gatsby-plugin-react-helmet`)

Keep authentication tokens and endpoint URLs in environment-specific files (`.env.development`, `.env.production`) and reference them via `process.env` to keep credentials out of version control.

```javascript
// gatsby-config.js
module.exports = {
  siteMetadata: {
    title: `My Large Site`,
    description: `A Gatsby site with many data sources`,
  },
  plugins: [
    // ── Source plugins ───────────────────────
    {
      resolve: `gatsby-source-wordpress`,
      options: {
        url: process.env.WP_GRAPHQL_ENDPOINT,
      },
    },
    {
      resolve: `gatsby-source-contentful`,
      options: {
        spaceId: process.env.CONTENTFUL_SPACE_ID,
        accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
      },
    },
    {
      resolve: `gatsby-source-stripe`,
      options: {
        objects: [`Price`, `Product`],
        secretKey: process.env.STRIPE_SECRET_KEY,
      },
    },

    // ── Transformer / UI plugins ──────────────
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-image`,
      options: {
        defaults: {
          placeholder: `blurred`,
          formats: [`auto`, `webp`],
        },
      },
    },
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-sitemap`,
  ],
}

```

## Encapsulate Complexity with Themes and Local Plugins

When multiple plugins always operate together (e.g., a blog setup requiring `gatsby-source-contentful`, image processing, and SEO utilities), bundle them into a **Gatsby theme**. Themes are essentially npm packages that export a [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js), allowing you to share complex configurations across projects. The conceptual documentation in [`docs/docs/conceptual/plugins-themes-and-starters.md`](https://github.com/gatsbyjs/gatsby/blob/main/docs/docs/conceptual/plugins-themes-and-starters.md) provides implementation patterns for creating composable themes.

For project-specific data sourcing that does not warrant a standalone npm package, use **local plugins** placed in the `/plugins` directory. This keeps custom sourcing logic version-controlled with your site while maintaining modularity.

A local plugin requires a standard [`package.json`](https://github.com/gatsbyjs/gatsby/blob/main/package.json) and a [`gatsby-node.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-node.js) file:

```bash
my-gatsby-site/
└─ plugins/
   └─ gatsby-plugin-my-cms/
      ├─ package.json
      └─ src/
         └─ gatsby-node.js

```

[`plugins/gatsby-plugin-my-cms/package.json`](https://github.com/gatsbyjs/gatsby/blob/main/plugins/gatsby-plugin-my-cms/package.json):

```json
{
  "name": "gatsby-plugin-my-cms",
  "main": "src/gatsby-node.js",
  "license": "MIT"
}

```

[`plugins/gatsby-plugin-my-cms/src/gatsby-node.js`](https://github.com/gatsbyjs/gatsby/blob/main/plugins/gatsby-plugin-my-cms/src/gatsby-node.js):

```javascript
exports.sourceNodes = async ({ actions, createNodeId, createContentDigest }, options) => {
  const { createNode } = actions
  
  const data = await fetch(options.apiUrl).then(r => r.json())

  data.forEach(item => {
    const node = {
      id: createNodeId(`my-cms-${item.id}`),
      parent: null,
      children: [],
      internal: {
        type: `MyCmsItem`,
        contentDigest: createContentDigest(item),
      },
      ...item,
    }
    createNode(node)
  })
}

```

Reference the local plugin in [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js) using `require.resolve`:

```javascript
{
  resolve: require.resolve(`./plugins/gatsby-plugin-my-cms`),
  options: {
    apiUrl: process.env.MY_CMS_ENDPOINT,
  },
}

```

## Optimize Build Performance with Parallel Sourcing

Starting with Gatsby 2.29, source plugins execute in parallel during the sourcing phase, dramatically reducing build times for sites with multiple network-bound data sources. No code changes are required to enable this behavior, but ensure your source plugins are compatible with parallel execution. This optimization is documented in [`docs/docs/reference/release-notes/v2.29/index.md`](https://github.com/gatsbyjs/gatsby/blob/main/docs/docs/reference/release-notes/v2.29/index.md).

For sites with high node churn (frequent additions and deletions), enable **stateful source nodes** (available in Gatsby 5.9+) to skip Gatsby’s expensive stale-node garbage collection. If your custom source plugin fully manages node lifecycles—including explicit deletion of removed nodes—invoke the `enableStatefulSourceNodes` action in the `onPreInit` lifecycle.

```javascript
// gatsby-node.js in your source plugin or site's gatsby-node.js
exports.onPreInit = ({ actions }) => {
  actions.enableStatefulSourceNodes()
}

```

This feature, detailed in [`docs/docs/reference/release-notes/v5.9/index.md`](https://github.com/gatsbyjs/gatsby/blob/main/docs/docs/reference/release-notes/v5.9/index.md), significantly reduces memory pressure on large sites by preventing Gatsby from diffing the entire node store against previous builds.

## Separate Data Sourcing from Page Creation

Maintain strict separation between data fetching and UI rendering. Implement all data sourcing logic within `sourceNodes` (either in source plugins or local plugins), while keeping page-routing logic in the site's [`gatsby-node.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-node.js) using the `createPages` API.

As documented in [`docs/docs/reference/config-files/gatsby-node.md`](https://github.com/gatsbyjs/gatsby/blob/main/docs/docs/reference/config-files/gatsby-node.md), the `createPages` function should query the GraphQL layer after all plugins have finished sourcing, then map results to templates stored in `/src/templates/`. This decoupling prevents UI components from being entangled with data-fetching concerns.

A typical large project structure looks like this:

```text
my-gatsby-site/
├─ .cache/                 # auto-generated, ignored by Git

├─ public/                 # auto-generated, ignored by Git

├─ src/
│   ├─ api/                # optional serverless functions

│   ├─ components/         # UI components

│   ├─ pages/              # file-system routing

│   ├─ templates/          # page templates for createPages

│   └─ gatsby-plugin-theme-ui/  # optional theme overrides

├─ plugins/                # local custom plugins

│   └─ gatsby-plugin-my-cms/
├─ gatsby-config.js        # central plugin configuration

├─ gatsby-node.js          # page creation logic

├─ gatsby-browser.js       # browser APIs

├─ gatsby-ssr.js           # server-side rendering APIs

└─ README.md               # plugin inventory and setup instructions

```

## Summary

- **Preserve canonical directories**: Keep `/.cache` and `/public` gitignored, source code in `/src`, and custom plugins in `/plugins`.
- **Group plugins by function**: Order source plugins before transformers in [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js), and externalize credentials to environment variables.
- **Use themes for reuse**: Bundle related plugins into themes for cross-project sharing; use local plugins for single-project logic.
- **Enable performance flags**: Ensure you are on Gatsby 2.29+ for parallel sourcing, and use `enableStatefulSourceNodes` (Gatsby 5.9+) for large, stateful data sources.
- **Decouple concerns**: Handle data in `sourceNodes` and routing in `createPages`, storing templates in `/src/templates/`.

## Frequently Asked Questions

### How should I structure the `plugins` directory for a large Gatsby site?

Create a top-level `/plugins` folder at the project root. Inside, create a subdirectory for each local plugin (e.g., `/plugins/gatsby-source-custom-api`) containing its own [`package.json`](https://github.com/gatsbyjs/gatsby/blob/main/package.json) and [`gatsby-node.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-node.js). Gatsby automatically recognizes these as resolvable plugins when you reference them with `require.resolve('./plugins/gatsby-source-custom-api')` in [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js).

### What is the difference between a Gatsby theme and a local plugin?

A **theme** is an npm package that encapsulates a reusable [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js), plugins, and components, intended for distribution across multiple sites. A **local plugin** resides in your project's `/plugins` directory and is specific to that codebase. Use themes for shareable architectural patterns (like a company-wide blog setup) and local plugins for project-specific data sourcing that does not need external publication.

### How can I speed up builds when using multiple source plugins?

First, upgrade to Gatsby 2.29 or later to leverage **parallel sourcing**, where Gatsby automatically runs `sourceNodes` for each plugin concurrently. Second, if your source plugin manages node deletions internally, enable **stateful source nodes** by calling `actions.enableStatefulSourceNodes()` in `onPreInit` (requires Gatsby 5.9+). This bypasses Gatsby's default node diffing algorithm, reducing memory overhead and build time for large datasets.

### Where should I configure environment-specific variables for source plugins?

Define environment variables in `.env.development` and `.env.production` files, then reference them in [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js) via `process.env.VARIABLE_NAME`. This keeps sensitive tokens out of version control while keeping the configuration centralized in [`gatsby-config.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-config.js) rather than scattered throughout your source code.