Skip to content

Add example using react + tailwind + shadcn #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions examples/react-shadcn/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.output
stats.html
stats-*.json
.wxt
web-ext.config.ts

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
230 changes: 230 additions & 0 deletions examples/react-shadcn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
---
name: React with Tailwind & shadcn
description: Simple example extension using React, Tailwind CSS, and shadcn UI components.
---

# WXT + React + Tailwind + Shadcn

This example demonstrates how to integrate React 19+, Tailwind CSS v4+, and shadcn UI components within a WXT extension.

## Installation Walkthrough

1. **Initialize a new WXT project:**

Open your terminal and run the following command to create a new WXT project with the React template:

```sh
pnpm dlx wxt@latest init
```

The CLI will guide you through the project setup. Choose the `react` template and your preferred package manager. For this example, I use pnpm.

```
WXT 0.20.6
ℹ Initializing new project
✔ Project Directory … react-shadcn
✔ Choose a template › react
✔ Package Manager › pnpm
✔ Downloading template
✨ WXT project created with the react template.
Next steps:
1. cd react-shadcn
2. pnpm install
```

2. **Navigate to the project directory and install dependencies:**

```sh
cd react-shadcn
pnpm install
```

3. **Install Tailwind CSS and `@tailwindcss/vite`:**

You should follow the official Tailwind Vite installation [guide](https://tailwindcss.com/docs/installation/using-vite). As the time of creating this example, it asked to run the following command:

```sh
pnpm install tailwindcss @tailwindcss/vite
```

4. **Configure Tailwind CSS in `wxt.config.ts`:**

To configure Tailwind CSS, modify `wxt.config.ts`. While official documentation says to change `vite.config.ts`, WXT configures Vite internally, so you need to update `wxt.config.ts` instead. This file manages the build process. To integrate Tailwind, add it as a Vite plugin within the wxt.config.ts file, as shown here:

```ts
import { defineConfig } from "wxt";
import tailwindcss from "@tailwindcss/vite";
// See https://wxt.dev/api/config.html
export default defineConfig({
modules: ["@wxt-dev/module-react"],
vite: () => ({
plugins: [tailwindcss()],
}),
});
```

5. **Create a `tailwind.css` file:**

Create a `tailwind.css` file in your `assets` directory (or the root directory of your project if you don't have an assets dir) with the following content:

```css
@import "tailwindcss";
```

6. **Import `tailwind.css`:**

You can now easily import the `tailwind.css` file in your React components:

```ts
import "@/assets/tailwind.css"; // Adjust the path if necessary
```

or you can include it directly in your `index.html` file:

```html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="@/assets/tailwind.css" rel="stylesheet" />
</head>
<body></body>
</html>
```

Now you can start styling your components with Tailwind CSS classes.

7. **Install and Configure Shadcn UI:**

Integrating Shadcn UI requires a few extra steps. You can choose either the [manual installation](https://ui.shadcn.com/docs/installation/manual) or the [Vite installation](https://ui.shadcn.com/docs/installation/vite) method. Both of them have workarounds we need to do, however this guide will use the Vite installation method.

You also need to decide whether to stick with WXT's default project structure or introduce a `src/` directory to separate source code from configuration files. WXT provides documentation on adding a `src/` directory [here](https://wxt.dev/guide/essentials/project-structure.html#adding-a-src-directory). This guide will continue without a `src/` directory for simplicity.

8. **Configure `tsconfig.json`:**

Before installing Shadcn UI components, you need to configure your `tsconfig.json` file. Add the following within the `compilerOptions` section:

```json
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"] // or "./src/*" if using src directory
}
}
```

9. **Configure `wxt.config.ts` for Alias Resolution:**

Update your `wxt.config.ts` to include an alias for resolving paths. Make sure to install `@types/node` for the `path` module: `pnpm add -D @types/node`

```ts
import { defineConfig } from "wxt";
import tailwindcss from "@tailwindcss/vite";
import path from "path";

// See https://wxt.dev/api/config.html
export default defineConfig({
modules: ["@wxt-dev/module-react"],
vite: () => ({
plugins: [tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./"), // or "./src" if using src directory
},
},
}),
});
```

10. **Temporarily Add `vite.config.ts` (Workaround for Shadcn CLI):**

The Shadcn CLI relies on detecting a `vite.config.ts` file to identify which framework to use. So before initializing the tool, we have to temporarily create a `vite.config.ts` file with the following content:

```ts
import path from "path";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./"), // or "./src" if using src directory
},
},
});
```

This file ensures that the Shadcn CLI correctly identifies your project as a Vite project and configures the alias. **This file should be deleted after the initialization.**

11. **Initialize Shadcn UI:**

Run the Shadcn UI initialization command:

```sh
pnpm dlx shadcn-ui@latest init
```

Answer the prompts in the CLI to configure Shadcn UI according to your preferences (color scheme, etc.).

12. **Delete Temporary `vite.config.ts`:**

After Shadcn UI is initialized, you can safely delete the temporary `vite.config.ts` file you created in step 10.

13. **Add Shadcn UI Components:**

You can now add Shadcn UI components using the CLI:

```sh
pnpm dlx shadcn-ui@latest add button
```

This will install the button component and its dependencies. Repeat this command for any other components you wish to use.

## Notes

There are some potential conflicts with WXT's recommended configuration and best practices in this setup, particularly in `wxt.config.ts` and `tsconfig.json`.

WXT advises against directly adding paths to `tsconfig.json` and prefers using the `alias` option in `wxt.config.ts` (see [WXT documentation](https://wxt.dev/guide/essentials/config/typescript.html#tsconfig-paths)). However, Shadcn currently fails to resolve paths correctly if they are only defined in `wxt.config.ts`. There is an [open issue](https://github.com/shadcn-ui/ui/issues/6020) about this in the Shadcn UI repository.

**Therefore, the current approach of modifying both `tsconfig.json` and `wxt.config.ts` is a temporary workaround.**

Ideally, the configuration should look like this:

```diff
// tsconfig.ts
{
"extends": "./.wxt/tsconfig.json",
"compilerOptions": {
"allowImportingTsExtensions": true,
"jsx": "react-jsx",
- "baseUrl": ".",
- "paths": {
- "@/*": ["./*"]
- }
}
}
```

```diff
// wxt.config.ts
export default defineConfig({
modules: ["@wxt-dev/module-react"],
+ alias: {
+ "@": path.resolve(__dirname, "./"),
+ },
vite: () => ({
plugins: [tailwindcss()],
- resolve: {
- alias: {
- "@": path.resolve(__dirname, "./"),
- },
- },
}),
});
```

However, this will not work correctly with Shadcn UI until the linked issue is resolved. Remember to monitor the linked issue in the Shadcn UI repository and update your configuration when a fix is available.

For a more in-depth guide that goes through the manual installation process, please check this [detailed guide](https://aabidk.dev/tags/wxt/).
1 change: 1 addition & 0 deletions examples/react-shadcn/assets/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
120 changes: 120 additions & 0 deletions examples/react-shadcn/assets/tailwind.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
@import "tailwindcss";
@import "tw-animate-css";

@custom-variant dark (&:is(.dark *));

@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}

:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}

.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}

@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
Loading