diff --git a/.changeset/angry-dancers-unite.md b/.changeset/angry-dancers-unite.md
new file mode 100644
index 00000000..c59baa0b
--- /dev/null
+++ b/.changeset/angry-dancers-unite.md
@@ -0,0 +1,5 @@
+---
+"@gql.tada/cli-utils": minor
+---
+
+Allow `generate-schema` to accept globs or multiple paths/globs. The command will therefore now be able to merge separate SDL files into a single one.
diff --git a/.changeset/two-parents-tie.md b/.changeset/two-parents-tie.md
new file mode 100644
index 00000000..35d691c7
--- /dev/null
+++ b/.changeset/two-parents-tie.md
@@ -0,0 +1,5 @@
+---
+"@gql.tada/internal": minor
+---
+
+Support globs and arrays of paths/globs to be passed to the `schema` option. This allows the local schema to be extended or a schema with split SDL files to be configured with `gql.tada` without any extra tooling.
diff --git a/packages/cli-utils/src/commands/generate-schema/index.ts b/packages/cli-utils/src/commands/generate-schema/index.ts
index 21c13f04..c52b0332 100644
--- a/packages/cli-utils/src/commands/generate-schema/index.ts
+++ b/packages/cli-utils/src/commands/generate-schema/index.ts
@@ -24,9 +24,9 @@ const parseHeaders = (
 export class GenerateSchema extends Command {
   static paths = [['generate-schema'], ['generate', 'schema']];
 
-  input = Option.String({
+  input = Option.Rest({
     name: 'schema',
-    required: true,
+    required: 1,
   });
 
   tsconfig = Option.String('--tsconfig,-c', {
diff --git a/packages/cli-utils/src/commands/generate-schema/runner.ts b/packages/cli-utils/src/commands/generate-schema/runner.ts
index bfad005c..55f683e0 100644
--- a/packages/cli-utils/src/commands/generate-schema/runner.ts
+++ b/packages/cli-utils/src/commands/generate-schema/runner.ts
@@ -11,7 +11,7 @@ import * as logger from './logger';
 
 export interface SchemaOptions {
   /** The filename to a `.graphql` SDL file, introspection JSON, or URL to a GraphQL API to introspect. */
-  input: string;
+  input: string | string[];
   /** Object of headers to send when introspection a GraphQL API. */
   headers: Record<string, string> | undefined;
   /** The filename to write the GraphQL SDL file to.
@@ -23,7 +23,9 @@ export interface SchemaOptions {
 }
 
 export async function* run(tty: TTY, opts: SchemaOptions): AsyncIterable<ComposeInput> {
-  const origin = opts.headers ? { url: opts.input, headers: opts.headers } : opts.input;
+  const origin = opts.headers
+    ? { url: Array.isArray(opts.input) ? opts.input[0] : opts.input, headers: opts.headers }
+    : opts.input;
   const loader = load({ rootPath: process.cwd(), origin });
 
   let schema: GraphQLSchema;
@@ -52,7 +54,8 @@ export async function* run(tty: TTY, opts: SchemaOptions): AsyncIterable<Compose
     if (
       'schema' in pluginConfig &&
       typeof pluginConfig.schema === 'string' &&
-      path.extname(pluginConfig.schema) === '.graphql'
+      (path.extname(pluginConfig.schema) === '.graphql' ||
+        path.extname(pluginConfig.schema) === '.gql')
     ) {
       destination = path.resolve(path.dirname(configResult.configPath), pluginConfig.schema);
     } else if (!('schema' in pluginConfig)) {
diff --git a/packages/internal/src/config.ts b/packages/internal/src/config.ts
index e5027945..9c3a50d4 100644
--- a/packages/internal/src/config.ts
+++ b/packages/internal/src/config.ts
@@ -48,6 +48,11 @@ const parseSchemaConfig = (input: unknown, rootPath: string): SchemaConfig => {
     throw new TadaError(`Schema is not configured properly (Received: ${input})`);
   }
 
+  if ('schema' in input && input.schema && Array.isArray(input.schema)) {
+    if (input.schema.some((include) => typeof include !== 'string')) {
+      throw new TadaError('All entries in `schema` array must be file paths');
+    }
+  }
   if ('schema' in input && input.schema && typeof input.schema === 'object') {
     const { schema } = input;
     if (!('url' in schema)) {
diff --git a/packages/internal/src/loaders/index.ts b/packages/internal/src/loaders/index.ts
index 90e8a9a1..89d53a3a 100644
--- a/packages/internal/src/loaders/index.ts
+++ b/packages/internal/src/loaders/index.ts
@@ -1,6 +1,5 @@
 export type * from './types';
 
-import path from 'node:path';
 import { loadFromSDL } from './sdl';
 import { loadFromURL } from './url';
 
@@ -18,7 +17,7 @@ export { loadFromSDL, loadFromURL };
 
 export const getURLConfig = (origin: SchemaOrigin | null) => {
   try {
-    return origin
+    return origin && !Array.isArray(origin)
       ? {
           url: new URL(typeof origin === 'object' ? origin.url : origin),
           headers: typeof origin === 'object' ? origin.headers : undefined,
@@ -42,13 +41,12 @@ export function load(config: LoadConfig): SchemaLoader {
       interval: config.fetchInterval,
       name: config.name,
     });
-  } else if (typeof config.origin === 'string') {
-    const file = config.rootPath ? path.resolve(config.rootPath, config.origin) : config.origin;
-    const assumeValid = config.assumeValid != null ? config.assumeValid : true;
+  } else if (typeof config.origin === 'string' || Array.isArray(config.origin)) {
     return loadFromSDL({
-      file,
-      assumeValid,
+      assumeValid: config.assumeValid != null ? config.assumeValid : true,
       name: config.name,
+      rootPath: config.rootPath,
+      include: config.origin,
     });
   } else {
     throw new Error(`Configuration contains an invalid "schema" option`);
@@ -61,7 +59,7 @@ export function loadRef(
   const teardowns: (() => void)[] = [];
 
   let _loaders: { input: SingleSchemaInput; loader: SchemaLoader }[] | undefined;
-  const getLoaders = (config?: BaseLoadConfig) => {
+  const getLoaders = (config: BaseLoadConfig) => {
     if (!_loaders) {
       _loaders = (('schemas' in input && input.schemas) || []).map((input) => ({
         input,
diff --git a/packages/internal/src/loaders/sdl.ts b/packages/internal/src/loaders/sdl.ts
index 5ed2db13..62ac8c0c 100644
--- a/packages/internal/src/loaders/sdl.ts
+++ b/packages/internal/src/loaders/sdl.ts
@@ -1,6 +1,7 @@
 import type { IntrospectionQuery } from 'graphql';
 import { buildSchema, buildClientSchema, executeSync } from 'graphql';
 import { CombinedError } from '@urql/core';
+import ts from 'typescript';
 import fs from 'node:fs/promises';
 import path from 'node:path';
 
@@ -8,22 +9,45 @@ import { makeIntrospectionQuery, getPeerSupportedFeatures } from './introspectio
 
 import type { SchemaLoader, SchemaLoaderResult, OnSchemaUpdate } from './types';
 
+const EXTENSIONS = ['.graphql', '.gql', '.json'] as const;
+const EXCLUDE = ['**/node_modules'] as const;
+
+const readInclude = (rootPath: string, include: string[] | string) => {
+  const files = ts.sys.readDirectory(
+    rootPath,
+    EXTENSIONS,
+    EXCLUDE,
+    typeof include === 'string' ? [include] : include
+  );
+  if (files.length === 0) {
+    throw new Error(`No schema input was found at "${rootPath}".`);
+  } else if (files.length > 1 && files.some((file) => path.extname(file) === '.json')) {
+    throw new Error(
+      'Multiple schema inputs were passed, but at least one is a JSON file.\n' +
+        'A JSON introspection schema cannot be combined with other schemas.'
+    );
+  }
+  return files;
+};
+
 interface LoadFromSDLConfig {
+  rootPath: string;
+  include: string | string[];
   name?: string;
   assumeValid?: boolean;
-  file: string;
 }
 
 export function loadFromSDL(config: LoadFromSDLConfig): SchemaLoader {
   const subscriptions = new Set<OnSchemaUpdate>();
 
+  let files: string[] | undefined;
   let controller: AbortController | null = null;
   let result: SchemaLoaderResult | null = null;
 
   const load = async (): Promise<SchemaLoaderResult> => {
-    const ext = path.extname(config.file);
-    const data = await fs.readFile(config.file, { encoding: 'utf8' });
-    if (ext === '.json') {
+    files = readInclude(config.rootPath, config.include);
+    if (path.extname(files[0]) === '.json') {
+      const data = await fs.readFile(files[0], { encoding: 'utf8' });
       const introspection = JSON.parse(data) as IntrospectionQuery | null;
       if (!introspection || !introspection.__schema) {
         throw new Error(
@@ -39,6 +63,9 @@ export function loadFromSDL(config: LoadFromSDLConfig): SchemaLoader {
         schema: buildClientSchema(introspection, { assumeValid: !!config.assumeValid }),
       };
     } else {
+      const data = (await Promise.all(files.map((file) => fs.readFile(file, 'utf-8')))).join(
+        '\n\n'
+      );
       const schema = buildSchema(data, { assumeValidSDL: !!config.assumeValid });
       const query = makeIntrospectionQuery(getPeerSupportedFeatures());
       const queryResult = executeSync({ schema, document: query });
@@ -60,21 +87,26 @@ export function loadFromSDL(config: LoadFromSDLConfig): SchemaLoader {
   };
 
   const watch = async () => {
+    if (!files) return;
     controller = new AbortController();
-    const watcher = fs.watch(config.file, {
-      signal: controller.signal,
-      persistent: false,
-    });
-    try {
-      for await (const _event of watcher) {
-        if ((result = await load())) {
-          for (const subscriber of subscriptions) subscriber(result);
+    for (const file of files) {
+      const watcher = fs.watch(file, {
+        signal: controller!.signal,
+        persistent: false,
+      });
+      (async () => {
+        try {
+          for await (const _event of watcher) {
+            if ((result = await load())) {
+              for (const subscriber of subscriptions) subscriber(result);
+            }
+          }
+        } catch (error: any) {
+          if (error.name !== 'AbortError') throw error;
+        } finally {
+          controller = null;
         }
-      }
-    } catch (error: any) {
-      if (error.name !== 'AbortError') throw error;
-    } finally {
-      controller = null;
+      })();
     }
   };
 
diff --git a/packages/internal/src/loaders/types.ts b/packages/internal/src/loaders/types.ts
index 51b0ad5a..3c73d183 100644
--- a/packages/internal/src/loaders/types.ts
+++ b/packages/internal/src/loaders/types.ts
@@ -26,7 +26,7 @@ export interface SchemaLoader {
 }
 
 export interface BaseLoadConfig {
-  rootPath?: string;
+  rootPath: string;
   fetchInterval?: number;
   assumeValid?: boolean;
 }
@@ -56,6 +56,7 @@ export interface SchemaRef<Result = SchemaLoaderResult | null> {
 
 export type SchemaOrigin =
   | string
+  | string[]
   | {
       url: string;
       headers?: HeadersInit;
diff --git a/website/reference/config-format.md b/website/reference/config-format.md
index 2726848c..1b1c16b2 100644
--- a/website/reference/config-format.md
+++ b/website/reference/config-format.md
@@ -86,7 +86,7 @@ the main plugin config or inside the `schemas[]` array items.
 The `schema` option specifies how to load your GraphQL schema and currently allows
 for three different schema formats. It accepts either:
 
-- a path to a `.graphql` file containing a schema definition (in GraphQL SDL format)
+- a path or glob to a `.graphql` file containing a schema definition (in GraphQL SDL format)
 - a path to a `.json` file containing a schema’s introspection query data
 - a URL to a GraphQL API that can be introspected
 
@@ -149,6 +149,10 @@ for three different schema formats. It accepts either:
 ```
 :::
 
+If your schema consists of multiple `.graphql` files, you may pass
+an array of paths, a glob, or an array of mixed paths and globs
+to your SDL files.
+
 This option is used by both the `gql.tada` CLI and `@0no-co/graphqlsp`
 and is required for all diagnostics and in-editor support to work.
 
diff --git a/website/reference/gql-tada-cli.md b/website/reference/gql-tada-cli.md
index eb98708b..094eee6b 100644
--- a/website/reference/gql-tada-cli.md
+++ b/website/reference/gql-tada-cli.md
@@ -68,12 +68,12 @@ When this command is run inside a GitHub Action, [workflow commands](https://doc
 
 ### `generate-schema`
 
-| Option          | Description                                                                                          |
-| --------------- | ---------------------------------------------------------------------------------------------------- |
-| `schema`        | URL to a GraphQL API or a path to a `.graphql` SDL file or introspection JSON.                       |
-| `--tsconfig,-c` | Optionally, a `tsconfig.json` file to use instead of an automatically discovered one.                |
-| `--output,-o`   | An output location to write the `.graphql` SDL file to. (Default: The `schema` configuration option) |
-| `--header,`     | A `key:value` header entry to use when retrieving the introspection from a GraphQL API.              |
+| Option          | Description                                                                                           |
+| --------------- | ----------------------------------------------------------------------------------------------------- |
+| `schema`        | URL to a GraphQL API or a path/glob to `.graphql` SDL files, or a path to an introspection JSON file. |
+| `--tsconfig,-c` | Optionally, a `tsconfig.json` file to use instead of an automatically discovered one.                 |
+| `--output,-o`   | An output location to write the `.graphql` SDL file to. (Default: The `schema` configuration option)  |
+| `--header,`     | A `key:value` header entry to use when retrieving the introspection from a GraphQL API.               |
 
 Oftentimes, an API may not be running in development, is maintained in a separate repository, or requires authorization headers, and specifying a URL in the `schema` configuration can slow down development.
 
@@ -121,12 +121,12 @@ When this command is run inside a GitHub Action, [workflow commands](https://doc
 
 ### `generate-persisted`
 
-| Option              | Description                                                                                      |
-| ------------------- | ------------------------------------------------------------------------------------------------ |
-| `--disable-normalization`     | Whether to disable normalizing the GraphQL document. (Default: false) |
-| `--tsconfig,-c`     | Optionally, a `tsconfig.json` file to use instead of an automatically discovered one.            |
-| `--fail-on-warn,-w` | Triggers an error and a non-zero exit code if any warnings have been reported.                   |
-| `--output,-o`       | Specify where to output the file to. (Default: The `tadaPersistedLocation` configuration option) |
+| Option                    | Description                                                                                      |
+| ------------------------- | ------------------------------------------------------------------------------------------------ |
+| `--disable-normalization` | Whether to disable normalizing the GraphQL document. (Default: false)                            |
+| `--tsconfig,-c`           | Optionally, a `tsconfig.json` file to use instead of an automatically discovered one.            |
+| `--fail-on-warn,-w`       | Triggers an error and a non-zero exit code if any warnings have been reported.                   |
+| `--output,-o`             | Specify where to output the file to. (Default: The `tadaPersistedLocation` configuration option) |
 
 The `gql.tada generate-persisted` command will scan your code for `graphql.persisted()` calls and generate
 a JSON manifest file containing a mapping of document IDs to the GraphQL document strings.
@@ -171,13 +171,13 @@ await generateOutput({
 
 ### `generatePersisted()`
 
-|                     | Description                                                                                                           |
-| ------------------- | --------------------------------------------------------------------------------------------------------------------- |
-| `disableNormalization`     | Disables normalizing the GraphQL document |
-| `output` option     | The filename to write the persisted JSON manifest file to (Default: the `tadaPersistedLocation` configuration option) |
-| `tsconfig` option   | The `tsconfig.json` to use instead of an automatically discovered one.                                                |
-| `failOnWarn` option | Whether to throw an error instead of logging warnings.                                                                |
-| returns             | A `Promise` that resolves when the task completes.                                                                    |
+|                        | Description                                                                                                           |
+| ---------------------- | --------------------------------------------------------------------------------------------------------------------- |
+| `disableNormalization` | Disables normalizing the GraphQL document                                                                             |
+| `output` option        | The filename to write the persisted JSON manifest file to (Default: the `tadaPersistedLocation` configuration option) |
+| `tsconfig` option      | The `tsconfig.json` to use instead of an automatically discovered one.                                                |
+| `failOnWarn` option    | Whether to throw an error instead of logging warnings.                                                                |
+| returns                | A `Promise` that resolves when the task completes.                                                                    |
 
 The `generatePersisted()` function will scan your code for `graphql.persisted()` calls and generate
 a JSON manifest file containing a mapping of document IDs to the GraphQL document strings.
@@ -200,13 +200,13 @@ await generatePersisted({
 
 ### `generateSchema()`
 
-|                   | Description                                                                                            |
-| ----------------- | ------------------------------------------------------------------------------------------------------ |
-| `input` option    | The filename to a `.graphql` SDL file, introspection JSON, or URL to a GraphQL API to introspect.      |
-| `headers` option  | Optionally, an object of headers to send when introspecting a GraphQL API.                             |
-| `output` option   | The filename to write the persisted JSON manifest file to (Default: the `schema` configuration option) |
-| `tsconfig` option | The `tsconfig.json` to use instead of an automatically discovered one.                                 |
-| returns           | A `Promise` that resolves when the task completes.                                                     |
+|                   | Description                                                                                                         |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
+| `input` option    | The path/glob to `.graphql` SDL files, a path to an introspection JSON file, or URL to a GraphQL API to introspect. |
+| `headers` option  | Optionally, an object of headers to send when introspecting a GraphQL API.                                          |
+| `output` option   | The filename to write the persisted JSON manifest file to (Default: the `schema` configuration option)              |
+| `tsconfig` option | The `tsconfig.json` to use instead of an automatically discovered one.                                              |
+| returns           | A `Promise` that resolves when the task completes.                                                                  |
 
 The `generateSchema()` function introspects a targeted GraphQL API by URL, a `.graphql` SDL
 or introspection JSON file, and outputs a `.graphql` SDL file. Generating a `.graphql` SDL file is