Skip to content

feat(tailwind): Add css configs to Taliwind component#2926

Open
JBezerra wants to merge 4 commits intoresend:canaryfrom
JBezerra:feat/css-configs-tw-component
Open

feat(tailwind): Add css configs to Taliwind component#2926
JBezerra wants to merge 4 commits intoresend:canaryfrom
JBezerra:feat/css-configs-tw-component

Conversation

@JBezerra
Copy link

@JBezerra JBezerra commented Feb 2, 2026

Summary

Adds support for CSS-based configuration to the <Tailwind> component, aligning with Tailwind CSS v4's CSS-first approach.

This addresses the discussion in #2863

Changes

  • Added theme and utility prop to Tailwind component
  • Updated setupTailwind to process custom CSS configurations

Example Usage (Using Vite)

import customTheme from './theme.css?inline';

<Tailwind theme={customTheme}>
  <div className="bg-brand p-huge font-sans">
    Custom themed content
  </div>
</Tailwind>

Summary by cubic

Add CSS-first configuration to the Tailwind component with new theme and utility props. This brings Tailwind v4-style CSS configs while keeping support for the existing config prop.

  • New Features
    • theme prop: pass a CSS string with @theme variables to customize tokens (colors, fonts, spacing, etc.).
    • utility prop: pass a CSS string to define custom utility classes and animations.
    • Works with config: theme and utility can be combined with the existing config prop.
    • Internals: setupTailwind now accepts { config, cssConfigs } and loads custom CSS in theme and utilities layers.

Written for commit 4ab3e8a. Summary will update on new commits.

@vercel
Copy link
Contributor

vercel bot commented Feb 2, 2026

@JBezerra is attempting to deploy a commit to the resend Team on Vercel.

A member of the Team first needs to authorize it.

@changeset-bot
Copy link

changeset-bot bot commented Feb 2, 2026

⚠️ No Changeset found

Latest commit: 4ab3e8a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 2, 2026

Open in StackBlitz

npm i https://pkg.pr.new/resend/react-email/@react-email/tailwind@2926

commit: 4ab3e8a

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="playground/README.md">

<violation number="1" location="playground/README.md:11">
P3: Documentation path is inconsistent with the example and project structure; users are told to create `/emails` but templates live under `playground/emails`.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

### 1. Create an email template

Create a new file at `playground/emails/testing.tsx`
Create a new file at `/emails`
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Documentation path is inconsistent with the example and project structure; users are told to create /emails but templates live under playground/emails.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At playground/README.md, line 11:

<comment>Documentation path is inconsistent with the example and project structure; users are told to create `/emails` but templates live under `playground/emails`.</comment>

<file context>
@@ -8,9 +8,9 @@ It includes all components directly from source with a path alias import of `@re
 ### 1. Create an email template
 
-Create a new file at `playground/emails/testing.tsx` 
+Create a new file at `/emails` 
 
-```tsx emails/testing.tsx
</file context>
Fix with Cubic

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's not perfectly right. I'm taking the fair assumption that is it obvious that root is playground given the README lives on it.

Along with this, I've removed testing.tsx because that was simply wrong given the phrase is "Create a new file at [...]"

@JBezerra
Copy link
Author

JBezerra commented Feb 2, 2026

⚠️ No Changeset found

Latest commit: f526080

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Not sure of the process here, if this change should immediately bump the version. I leave this answer to @gabrielmfern.

@jasongerbes
Copy link

Could you please check whether this fixes compatibility with the Tailwind CSS IntelliSense VS Code plugin.

The IntelliSense plugin requires a .css file that imports a Tailwind CSS stylesheet (e.g. @import "tailwindcss").

When using React Email with Tailwind in a monorepo setup, no such .css file exists.

For non-monorepo setups, the IntelliSense plugin will resolve the application's .css file which may not match the config used by the <Tailwind> component (e.g. pixel based preset, custom theme overrides, etc).

@gabrielmfern gabrielmfern self-requested a review March 18, 2026 20:06
Copy link
Member

@gabrielmfern gabrielmfern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left some comments, for some small changes before we can merge


### 1. Create an email template

Create a new file at `playground/emails/testing.tsx`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is unrelated, can you revert?

@@ -1,2 +1,3 @@
**/*.tsx
**/*
# Ignore all files in the emails directory except for the example.tsx file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also unrelated, feel free to open a second PR with these changes, but I'd rather you revert them here

config?: TailwindConfig;
cssConfigs?: CSSConfigs;
}
export async function setupTailwind({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it should be either a config, or a css config? how does tailwind prioritize between them when they're both present?


expect(generate(getStyleSheet())).toMatchInlineSnapshot(
`"/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */@layer theme,base,components,utilities;@layer theme{:root,:host{--color-red-500: oklch(63.7% 0.237 25.331);--color-blue-300: oklch(80.9% 0.105 251.813);--color-slate-900: oklch(20.8% 0.042 265.755)}}@layer utilities{.bg-slate-900{background-color:var(--color-slate-900)}.text-red-500{color:var(--color-red-500)}.sm\\:bg-blue-300{@media (width>=40rem){background-color:var(--color-blue-300)}}}"`,
`"/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */@layer theme,base,components,utilities;@layer theme{:root,:host{--color-red-500: oklch(63.7% 0.237 25.331);--color-blue-300: oklch(80.9% 0.105 251.813);--color-slate-900: oklch(20.8% 0.042 265.755)}}@layer utilities{.bg-slate-900{background-color:var(--color-slate-900)}.text-red-500{color:var(--color-red-500)}.sm\\:bg-blue-300{@media (width>=40rem){background-color:var(--color-blue-300)}}}@layer theme;@layer utilities;"`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this change?

* Stringifies the data to a JSON string that is safe to use.
* It will replace functions with their string representation.
*/
function JSONStringify(data: object) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like having a separate function like this for something only used once

/**
* The pixel based preset is the preset recommended to use for emails.
* It is used to style the email with a pixel based system.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you revert this comment?


export interface TailwindProps {
children: React.ReactNode;
/** Tailwind config object. Used in Tailwind v3. */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment might confuse people, since it works in v4 and we don't support v3 at all anymore. I think it's better without

import { cloneElementWithInlinedStyles } from './utils/tailwindcss/clone-element-with-inlined-styles';
import { setupTailwind } from './utils/tailwindcss/setup-tailwind';

export type CSSString = string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you just inline string where this is used?

});
});

describe('with theme', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe with css configuration instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants