diff --git a/packages/plugin-dynamic-sitemap/README.md b/packages/plugin-dynamic-sitemap/README.md
new file mode 100644
index 000000000..33abb58b6
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/README.md
@@ -0,0 +1,24 @@
+# @greenwood/plugin-dynamic-sitemap
+
+## Overview
+Spiders love to spider. To show our love to all the spiders out there, this plugin reads
+the graph and renders a sitemap.xml. Currently, it handles up to 10000 content entries, warning
+after 9000 content entries.
+
+## Usage
+Add this plugin to your _greenwood.config.js_ and spread the `export`.
+
+```javascript
+import { greenwoodPluginDynamicExport } from '@greenwood/plugin-dynamic-sitemap';
+
+export default {
+ ...
+
+ plugins: [
+ greenwoodPluginDynamicExport({
+ "baseUrl": "https://example.com"
+ })
+ ]
+}
+```
+
diff --git a/packages/plugin-dynamic-sitemap/src/index.js b/packages/plugin-dynamic-sitemap/src/index.js
new file mode 100644
index 000000000..3c156d9cd
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/src/index.js
@@ -0,0 +1,30 @@
+import fs from 'fs/promises';
+
+const greenwoodPluginDynamicExport = (options = {}) => [{
+ type: 'copy',
+ name: 'plugin-dynamic-sitemap',
+ provider: async (compilation) => {
+
+ const { baseUrl } = options;
+ const { outputDir } = compilation.context;
+
+ let sitemapXML = '\n';
+ sitemapXML += '\n';
+
+ compilation.graph.forEach(page => {
+ sitemapXML += `${baseUrl}${page.outputPath}\n`;
+ });
+
+ sitemapXML += '';
+
+ const sitemapUrl = new URL('./sitemap.xml', outputDir);
+ await fs.writeFile(sitemapUrl, sitemapXML);
+
+ return {
+ from: sitemapUrl,
+ to: new URL('./sitemap.xml', outputDir)
+ };
+ }
+}];
+
+export { greenwoodPluginDynamicExport };
\ No newline at end of file
diff --git a/packages/plugin-dynamic-sitemap/test/copy.default.dynamic-sitemap.spec.js b/packages/plugin-dynamic-sitemap/test/copy.default.dynamic-sitemap.spec.js
new file mode 100644
index 000000000..196f4ccde
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/test/copy.default.dynamic-sitemap.spec.js
@@ -0,0 +1,74 @@
+/*
+ * Use Case
+ * Run Greenwood build command with no config and this plugin.
+ *
+ * User Result
+ * Should generate a bare bones Greenwood build with correctly built sitemap.xml
+ *
+ * User Command
+ * greenwood build
+ *
+ * User Config
+ * import { greenwoodPluginDynamicExport } from '@greenwood/plugin-dynamic-sitemap';
+ *
+ * {
+ * plugins: [{
+ * greenwoodPluginDynamicExport({
+ * "baseUrl": "https://example.com"
+ * })
+ * }]
+ *
+ * }
+ *
+ * User Workspace
+* src/
+* templates/
+* artist.html
+* pages/
+* index.md
+* about.md
+*/
+
+import fs from 'fs';
+import path from 'path';
+import { runSmokeTest } from '../../../test/smoke-test.js';
+import { getSetupFiles, getOutputTeardownFiles } from '../../../test/utils.js';
+import { Runner } from 'gallinago';
+import { fileURLToPath, URL } from 'url';
+
+describe('Build Greenwood With Dynamic Sitemap Plugin: ', function() {
+ const LABEL = 'Using Dynamic Sitemap feature';
+ const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js');
+ const outputPath = fileURLToPath(new URL('.', import.meta.url));
+ let runner;
+
+ before(function() {
+ this.context = {
+ publicDir: path.join(outputPath, 'public')
+ };
+ runner = new Runner();
+ });
+
+ describe(LABEL, function() {
+
+ before(function() {
+ runner.setup(outputPath, getSetupFiles(outputPath));
+ runner.runCommand(cliPath, 'build');
+ });
+
+ runSmokeTest(['public', 'index'], LABEL);
+
+ describe('Sitemap.xml should exist and be well formed', function() {
+
+ it('should have one sitemaps file in the output directory', function() {
+ const sitemapXML = fs.readFileSync(path.join(this.context.publicDir, './sitemap.xml'));
+ console.log(sitemapXML);
+ });
+ });
+
+ });
+
+ after(function() {
+ runner.teardown(getOutputTeardownFiles(outputPath));
+ });
+});
\ No newline at end of file
diff --git a/packages/plugin-dynamic-sitemap/test/greenwood.config.js b/packages/plugin-dynamic-sitemap/test/greenwood.config.js
new file mode 100644
index 000000000..20aa502cd
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/test/greenwood.config.js
@@ -0,0 +1,10 @@
+import { greenwoodPluginDynamicExport } from '../src/index.js';
+
+console.log(greenwoodPluginDynamicExport);
+export default {
+ plugins: [
+ ...greenwoodPluginDynamicExport({
+ 'baseUrl': 'https://example.com'
+ })
+ ]
+};
\ No newline at end of file
diff --git a/packages/plugin-dynamic-sitemap/test/package.json b/packages/plugin-dynamic-sitemap/test/package.json
new file mode 100644
index 000000000..1a1e6d1d1
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/test/package.json
@@ -0,0 +1,4 @@
+{
+ "version": "1.0.0",
+ "type": "module"
+}
\ No newline at end of file
diff --git a/packages/plugin-dynamic-sitemap/test/src/pages/about.md b/packages/plugin-dynamic-sitemap/test/src/pages/about.md
new file mode 100644
index 000000000..4093eb641
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/test/src/pages/about.md
@@ -0,0 +1,3 @@
+# About Us
+
+Lorem ipsum.
\ No newline at end of file
diff --git a/packages/plugin-dynamic-sitemap/test/src/pages/index.md b/packages/plugin-dynamic-sitemap/test/src/pages/index.md
new file mode 100644
index 000000000..46c5f357e
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/test/src/pages/index.md
@@ -0,0 +1,3 @@
+## Home Page
+
+Welcome!
\ No newline at end of file
diff --git a/packages/plugin-dynamic-sitemap/test/src/templates/artist.html b/packages/plugin-dynamic-sitemap/test/src/templates/artist.html
new file mode 100644
index 000000000..0087d30c1
--- /dev/null
+++ b/packages/plugin-dynamic-sitemap/test/src/templates/artist.html
@@ -0,0 +1,8 @@
+
+
+
+ Welcome to the artist page.
+
+
+
+
\ No newline at end of file