diff --git a/.changeset/rude-deers-turn.md b/.changeset/rude-deers-turn.md
new file mode 100644
index 000000000000..ee54f44163ee
--- /dev/null
+++ b/.changeset/rude-deers-turn.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/tailwind': minor
+---
+
+Adds `nesting` option to enable `tailwindcss/nesting` support
diff --git a/packages/integrations/tailwind/package.json b/packages/integrations/tailwind/package.json
index 34cda3a4c63e..8ef499dbeb07 100644
--- a/packages/integrations/tailwind/package.json
+++ b/packages/integrations/tailwind/package.json
@@ -29,7 +29,8 @@
   "scripts": {
     "build": "astro-scripts build \"src/**/*.ts\" && tsc",
     "build:ci": "astro-scripts build \"src/**/*.ts\"",
-    "dev": "astro-scripts dev \"src/**/*.ts\""
+    "dev": "astro-scripts dev \"src/**/*.ts\"",
+    "test": "mocha --exit --timeout 20000 test/"
   },
   "dependencies": {
     "autoprefixer": "^10.4.15",
@@ -39,6 +40,8 @@
   "devDependencies": {
     "astro": "workspace:*",
     "astro-scripts": "workspace:*",
+    "chai": "^4.3.7",
+    "mocha": "^10.2.0",
     "tailwindcss": "^3.3.5",
     "vite": "^5.0.10"
   },
diff --git a/packages/integrations/tailwind/src/index.ts b/packages/integrations/tailwind/src/index.ts
index 5f30ae4b67d6..1da44f5dd45b 100644
--- a/packages/integrations/tailwind/src/index.ts
+++ b/packages/integrations/tailwind/src/index.ts
@@ -1,3 +1,4 @@
+import { fileURLToPath } from 'node:url';
 import type { AstroIntegration } from 'astro';
 import autoprefixerPlugin from 'autoprefixer';
 import tailwindPlugin from 'tailwindcss';
@@ -23,15 +24,22 @@ async function getPostCssConfig(
 
 async function getViteConfiguration(
 	tailwindConfigPath: string | undefined,
-	viteConfig: UserConfig
+	nesting: boolean,
+	root: string,
+	postcssInlineOptions: CSSOptions['postcss']
 ): Promise<Partial<UserConfig>> {
 	// We need to manually load postcss config files because when inlining the tailwind and autoprefixer plugins,
 	// that causes vite to ignore postcss config files
-	const postcssConfigResult = await getPostCssConfig(viteConfig.root, viteConfig.css?.postcss);
+	const postcssConfigResult = await getPostCssConfig(root, postcssInlineOptions);
 
 	const postcssOptions = postcssConfigResult?.options ?? {};
 	const postcssPlugins = postcssConfigResult?.plugins?.slice() ?? [];
 
+	if (nesting) {
+		const tailwindcssNestingPlugin = (await import('tailwindcss/nesting/index.js')).default;
+		postcssPlugins.push(tailwindcssNestingPlugin());
+	}
+
 	postcssPlugins.push(tailwindPlugin(tailwindConfigPath));
 	postcssPlugins.push(autoprefixerPlugin());
 
@@ -59,18 +67,30 @@ type TailwindOptions = {
 	 * @default true
 	 */
 	applyBaseStyles?: boolean;
+	/**
+	 * Add CSS nesting support using `tailwindcss/nesting`. See {@link https://tailwindcss.com/docs/using-with-preprocessors#nesting Tailwind's docs}
+	 * for how this works with `postcss-nesting` and `postcss-nested`.
+	 */
+	nesting?: boolean;
 };
 
 export default function tailwindIntegration(options?: TailwindOptions): AstroIntegration {
 	const applyBaseStyles = options?.applyBaseStyles ?? true;
 	const customConfigPath = options?.configFile;
+	const nesting = options?.nesting ?? false;
+
 	return {
 		name: '@astrojs/tailwind',
 		hooks: {
 			'astro:config:setup': async ({ config, updateConfig, injectScript }) => {
 				// Inject the Tailwind postcss plugin
 				updateConfig({
-					vite: await getViteConfiguration(customConfigPath, config.vite),
+					vite: await getViteConfiguration(
+						customConfigPath,
+						nesting,
+						fileURLToPath(config.root),
+						config.vite.css?.postcss
+					),
 				});
 
 				if (applyBaseStyles) {
diff --git a/packages/integrations/tailwind/test/basic.test.js b/packages/integrations/tailwind/test/basic.test.js
new file mode 100644
index 000000000000..e5275c81e9d6
--- /dev/null
+++ b/packages/integrations/tailwind/test/basic.test.js
@@ -0,0 +1,33 @@
+import { expect } from 'chai';
+import { loadFixture } from '../../../astro/test/test-utils.js';
+
+describe('Basic', () => {
+	let fixture;
+
+	before(async () => {
+		fixture = await loadFixture({
+			root: new URL('./fixtures/basic/', import.meta.url),
+		});
+	});
+
+	describe('build', () => {
+		before(async () => {
+			await fixture.build();
+		});
+
+		it('works', async () => {
+			const astroChunkDir = await fixture.readdir('/_astro');
+
+			let css = '';
+			for (const file of astroChunkDir) {
+				if (file.endsWith('.css')) {
+					css += await fixture.readFile(`/_astro/${file}`);
+				}
+			}
+
+			expect(css).to.include('box-sizing:border-box;'); // base css
+			expect(css).to.include('text-red-500'); // class css
+			expect(css).to.match(/\.a\[data-astro-cid-.*?\] \.b\[data-astro-cid-.*?\]/); // nesting
+		});
+	});
+});
diff --git a/packages/integrations/tailwind/test/fixtures/basic/astro.config.js b/packages/integrations/tailwind/test/fixtures/basic/astro.config.js
new file mode 100644
index 000000000000..cd684d6eb94c
--- /dev/null
+++ b/packages/integrations/tailwind/test/fixtures/basic/astro.config.js
@@ -0,0 +1,12 @@
+import { fileURLToPath } from 'node:url';
+import { defineConfig } from 'astro/config';
+import tailwind from '@astrojs/tailwind';
+
+export default defineConfig({
+  integrations: [
+    tailwind({
+      configFile: fileURLToPath(new URL('./tailwind.config.js', import.meta.url)),
+      nesting: true
+    }),
+  ]
+});
diff --git a/packages/integrations/tailwind/test/fixtures/basic/package.json b/packages/integrations/tailwind/test/fixtures/basic/package.json
new file mode 100644
index 000000000000..33e20daaa380
--- /dev/null
+++ b/packages/integrations/tailwind/test/fixtures/basic/package.json
@@ -0,0 +1,10 @@
+{
+  "name": "@test/tailwind-basic",
+  "version": "0.0.0",
+  "private": true,
+  "type": "module",
+  "dependencies": {
+    "astro": "workspace:*",
+    "@astrojs/tailwind": "workspace:*"
+  }
+}
diff --git a/packages/integrations/tailwind/test/fixtures/basic/src/pages/index.astro b/packages/integrations/tailwind/test/fixtures/basic/src/pages/index.astro
new file mode 100644
index 000000000000..ab4df58e3305
--- /dev/null
+++ b/packages/integrations/tailwind/test/fixtures/basic/src/pages/index.astro
@@ -0,0 +1,13 @@
+<div class="text-red-500">red</div>
+
+<div class="a">
+  <div class="b">nested blue</div>
+</div>
+
+<style>
+  .a {
+    .b {
+      color: blue;
+    }
+  }
+</style>
diff --git a/packages/integrations/tailwind/test/fixtures/basic/tailwind.config.js b/packages/integrations/tailwind/test/fixtures/basic/tailwind.config.js
new file mode 100644
index 000000000000..f1090968169a
--- /dev/null
+++ b/packages/integrations/tailwind/test/fixtures/basic/tailwind.config.js
@@ -0,0 +1,6 @@
+import path from 'node:path';
+
+/** @type {import('tailwindcss').Config} */
+export default {
+	content: [path.join(__dirname, 'src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}')],
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0b64ed8dc673..0603b09027d9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4566,6 +4566,12 @@ importers:
       astro-scripts:
         specifier: workspace:*
         version: link:../../../scripts
+      chai:
+        specifier: ^4.3.7
+        version: 4.3.10
+      mocha:
+        specifier: ^10.2.0
+        version: 10.2.0
       tailwindcss:
         specifier: ^3.3.5
         version: 3.3.5
@@ -4573,6 +4579,15 @@ importers:
         specifier: ^5.0.10
         version: 5.0.10(@types/node@18.18.6)(sass@1.69.5)
 
+  packages/integrations/tailwind/test/fixtures/basic:
+    dependencies:
+      '@astrojs/tailwind':
+        specifier: workspace:*
+        version: link:../../..
+      astro:
+        specifier: workspace:*
+        version: link:../../../../../astro
+
   packages/integrations/vercel:
     dependencies:
       '@astrojs/internal-helpers':