Closed lunoob closed 6 months ago
Can you maybe elaborate more on what is happening to your email template? Does it work under some circumstances and others not, and if so, can you please tell me what circumstances?
Same problem here, with @react-email/components": "0.0.7"
my email templates were working well, after upgrade, a lot of Tailwind classes are not converted into style attributes anymore.
For example:
<Link href={t("common.appStore.href")} className="inline-block">
<Img
src={t("common.appStore.img")}
width="119"
height="40"
alt={t("common.appStore.label")}
className="max-w-full h-auto inline"
/>
</Link>
is generating
<a href="xxx" class="inline-block" style="color:#067df7;text-decoration:none" target="_blank"><img class="max-w-full h-auto inline" alt="xxx" height="40" src="xxx" style="display:block;outline:none;border:none;text-decoration:none" width="119"></a>
After some test, I found that the problem apply when I load custom components in an email template, for example:
import * as React from "react";
import MainLayout from "../components/ui/MainLayout";
import Email1 from "../components/views/Email1";
import { useGetToken } from "../hooks/useGetToken";
const locale = "en";
export const Email1En = () => {
const { t } = useGetToken(locale);
return (
<MainLayout
title={t("pnp23bf1_22.title")}
preview={t("pnp23bf1_22.preview")}
>
<Email1 locale={locale} />
</MainLayout>
);
};
export default Email1En;
import { Body, Head, Html, Preview } from "@react-email/components";
import React from "react";
import TailwindProvider from "./TailwindProvider";
interface MainLayoutProps {
title?: string;
preview?: string;
children?: React.ReactNode;
}
export const MainLayout = ({ title, preview, children }: MainLayoutProps) => {
return (
<Html>
<Head>
<title>{title}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="true"
/>
<link
href="https://fonts.googleapis.com/css2?family=Roboto+Slab&display=swap"
rel="stylesheet"
/>
<style>
{`
@media screen and (max-width: 600px) {
h1 {
font-size: 30px !important;
}
}
`}
</style>
</Head>
{preview && <Preview>{preview}</Preview>}
<TailwindProvider>
<Body className="text-gray-900 bg-white my-auto mx-auto font-sans leading-snug">
{children}
</Body>
</TailwindProvider>
</Html>
);
};
export default MainLayout;
Most Tailwind classes into <Email1 />
are not generated
Can you maybe elaborate more on what is happening to your email template? Does it work under some circumstances and others not, and if so, can you please tell me what circumstances?
yes, I maked an example
Going to take a look into fixing this to unblock you guys, a PR is coming that is going to improve a lot of things about the Tailwind component.
I think it might be related with recent react update. Essentially, they "fixed bug" when rendering functions take context into account.
Same problem here, with
@react-email/components": "0.0.7"
my email templates were working well, after upgrade, a lot of Tailwind classes are not converted into style attributes anymore.For example:
<Link href={t("common.appStore.href")} className="inline-block"> <Img src={t("common.appStore.img")} width="119" height="40" alt={t("common.appStore.label")} className="max-w-full h-auto inline" /> </Link>
is generating
<a href="xxx" class="inline-block" style="color:#067df7;text-decoration:none" target="_blank"><img class="max-w-full h-auto inline" alt="xxx" height="40" src="xxx" style="display:block;outline:none;border:none;text-decoration:none" width="119"></a>
After some test, I found that the problem apply when I load custom components in an email template, for example:
import * as React from "react"; import MainLayout from "../components/ui/MainLayout"; import Email1 from "../components/views/Email1"; import { useGetToken } from "../hooks/useGetToken"; const locale = "en"; export const Email1En = () => { const { t } = useGetToken(locale); return ( <MainLayout title={t("pnp23bf1_22.title")} preview={t("pnp23bf1_22.preview")} > <Email1 locale={locale} /> </MainLayout> ); }; export default Email1En;
import { Body, Head, Html, Preview } from "@react-email/components"; import React from "react"; import TailwindProvider from "./TailwindProvider"; interface MainLayoutProps { title?: string; preview?: string; children?: React.ReactNode; } export const MainLayout = ({ title, preview, children }: MainLayoutProps) => { return ( <Html> <Head> <title>{title}</title> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="true" /> <link href="https://fonts.googleapis.com/css2?family=Roboto+Slab&display=swap" rel="stylesheet" /> <style> {` @media screen and (max-width: 600px) { h1 { font-size: 30px !important; } } `} </style> </Head> {preview && <Preview>{preview}</Preview>} <TailwindProvider> <Body className="text-gray-900 bg-white my-auto mx-auto font-sans leading-snug"> {children} </Body> </TailwindProvider> </Html> ); }; export default MainLayout;
Most Tailwind classes into
<Email1 />
are not generated
Working on a fix for this on a new PR that also improves performance.
The problem here is that only the children passed through props to components are handled, while the resulting children of the rendered component are not.
This is indeed a bug and we are on it.
PR that solves this is basically done, this problem will go away soon guys.
If it takes too long for us to merge and release a new version out, I made a quick patch that fixes the issue as a workaround
diff --git a/dist/index.js b/dist/index.js
index 12a4d02aac2b2364deb79d7785bb5b2d735d9e12..12a042dd383a3e91cbfe803af11210c5b40457a7 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -154,6 +154,17 @@ function processElement(element, headStyles, twi) {
...processedChildren
);
}
+ if (typeof modifiedElement.type === "function") {
+ const component = modifiedElement.type;
+ const renderedComponent = component(modifiedElement.props);
+ if (React.isValidElement(renderedComponent)) {
+ modifiedElement = processElement(
+ renderedComponent,
+ headStyles,
+ twi
+ );
+ }
+ }
return modifiedElement;
}
function processHead(child, responsiveStyles) {
diff --git a/dist/index.mjs b/dist/index.mjs
index 2e58d8425e6bf5ed3928e99d20fd1e1006d70e36..3f7b69f98aaf9ab4c45c27153d55b774e6e326f8 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -121,6 +121,17 @@ function processElement(element, headStyles, twi) {
...processedChildren
);
}
+ if (typeof modifiedElement.type === "function") {
+ const component = modifiedElement.type;
+ const renderedComponent = component(modifiedElement.props);
+ if (React.isValidElement(renderedComponent)) {
+ modifiedElement = processElement(
+ renderedComponent,
+ headStyles,
+ twi
+ );
+ }
+ }
return modifiedElement;
}
function processHead(child, responsiveStyles) {
Still not working for me. But my workaround with this is to wrap the component in Tailwind as well.. maybe not the best.. but it works for now.. 😅
import { Img, Section, Tailwind } from '@react-email/components';
import * as React from 'react';
import tailwindConfig from '../../tailwind.config';
export const Header = () => (
<Tailwind config={tailwindConfig}>
<Section className="round flex h-[120px] items-center justify-center rounded-tl-lg rounded-tr-lg bg-wt-black">
<Img
src="src"
/>
</Section>
</Tailwind>
);
Still not working for me. But my workaround with this is to wrap the component in Tailwind as well.. maybe not the best.. but it works for now.. 😅
import { Img, Section, Tailwind } from '@react-email/components'; import * as React from 'react'; import tailwindConfig from '../../tailwind.config'; export const Header = () => ( <Tailwind config={tailwindConfig}> <Section className="round flex h-[120px] items-center justify-center rounded-tl-lg rounded-tr-lg bg-wt-black"> <Img src="src" /> </Section> </Tailwind> );
can you maybe give a reproduction so I can narrow down the problem on your case?
In my case its just that when I import this in to my email-template the tailwind styles dont get applied to the component.
import {
Body,
Button,
Column,
Container,
Head,
Heading,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Tailwind,
Text,
} from '@react-email/components';
import * as React from 'react';
import tailwindConfig from '../tailwind.config';
import { Header } from './components/Header';
import { Footer } from './components/Footer';
export const AssignedRole = () => {
return (
<Html>
<Head />
<Preview>{`Preview text`}</Preview>
<Tailwind config={tailwindConfig}>
<Body className="mx-auto my-auto rounded-lg bg-white font-sans">
<Container className="mx-auto my-[40px] w-[600px] rounded-lg border border-solid border-[#eaeaea]"> // Styles work
<Header /> // No styles without wrapping the component in Tailwind
// ALLL THE CONTENT and all styles work
<Footer /> // No styles without wrapping the component in Tailwind
</Container>
</Body>
</Tailwind>
</Html>
);
};
export default AssignedRole;
In my case its just that when I import this in to my email-template the tailwind styles dont get applied to the component.
import { Body, Button, Column, Container, Head, Heading, Hr, Html, Img, Link, Preview, Row, Section, Tailwind, Text, } from '@react-email/components'; import * as React from 'react'; import tailwindConfig from '../tailwind.config'; import { Header } from './components/Header'; import { Footer } from './components/Footer'; export const AssignedRole = () => { return ( <Html> <Head /> <Preview>{`Preview text`}</Preview> <Tailwind config={tailwindConfig}> <Body className="mx-auto my-auto rounded-lg bg-white font-sans"> <Container className="mx-auto my-[40px] w-[600px] rounded-lg border border-solid border-[#eaeaea]"> // Styles work <Header /> // No styles without wrapping the component in Tailwind // ALLL THE CONTENT and all styles work <Footer /> // No styles without wrapping the component in Tailwind </Container> </Body> </Tailwind> </Html> ); }; export default AssignedRole;
Could you try importing from @react-email/tailwind
instead of components?
I installed the Tailwind component and used it directly instead as you suggested but no styles get applied 🙁
I installed the Tailwind component and used it directly instead as you suggested but no styles get applied 🙁
how did you install the patch?
Ahh maybe that the issue. I just installed it like this: pnpm add @react-email/tailwind -E
"prettier-plugin-tailwindcss": "^0.5.7",
Ahh maybe that the issue. I just installed it like this:
pnpm add @react-email/tailwind -E
"prettier-plugin-tailwindcss": "^0.5.7",
As mentioned more above in the issue, we don't yet have a release with the issue fixed, but it is coming, so you'll have to use the patch for now. To use the patch you can either use pnpm
's built-in patching mechanism, or use something like patch-package.
If you prefer using pnpm, you will need to go through the following:
package.json
"pnpm": {
"patchedDependencies": {
"@react-email/tailwind@0.0.12": "patches/@react-email__tailwind@0.0.12.patch"
}
}
patches/@react-email__tailwind@0.0.12.patch
diff --git a/dist/index.js b/dist/index.js
index 12a4d02aac2b2364deb79d7785bb5b2d735d9e12..12a042dd383a3e91cbfe803af11210c5b40457a7 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -154,6 +154,17 @@ function processElement(element, headStyles, twi) {
...processedChildren
);
}
+ if (typeof modifiedElement.type === "function") {
+ const component = modifiedElement.type;
+ const renderedComponent = component(modifiedElement.props);
+ if (React.isValidElement(renderedComponent)) {
+ modifiedElement = processElement(
+ renderedComponent,
+ headStyles,
+ twi
+ );
+ }
+ }
return modifiedElement;
}
function processHead(child, responsiveStyles) {
diff --git a/dist/index.mjs b/dist/index.mjs
index 2e58d8425e6bf5ed3928e99d20fd1e1006d70e36..3f7b69f98aaf9ab4c45c27153d55b774e6e326f8 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -121,6 +121,17 @@ function processElement(element, headStyles, twi) {
...processedChildren
);
}
+ if (typeof modifiedElement.type === "function") {
+ const component = modifiedElement.type;
+ const renderedComponent = component(modifiedElement.props);
+ if (React.isValidElement(renderedComponent)) {
+ modifiedElement = processElement(
+ renderedComponent,
+ headStyles,
+ twi
+ );
+ }
+ }
return modifiedElement;
}
function processHead(child, responsiveStyles) {
pnpm install
and the patch should be applied and the component should work now. Something to note that is important is that if you are not using pnpm
somewhere the patch won't be applied, in a CI pipeline for example, so be sure to look out for that. A plus to patch-package
if you are just using normal npm.Also make sure you have @react-email/tailwind@0.0.12
installed since the patch is made for this version. For the patch-package
way, you can look into its docs and I believe its going to be pretty similar to the above.
For context, a patch would be a manual change to a installed module that has some kind of problem that has not been fixed on a new version, its a sad reality, but sometimes we need patches on certain modules.
So instead of needing to do it manually there are these tools (such as pnpm patch
or patch-package
) that help you do this easily and in a way that everyone that also installs the dependencies of the project does not have to go through the library code again or apply the change manually.
Let me know if this works for you
Ahh maybe that the issue. I just installed it like this:
pnpm add @react-email/tailwind -E
"prettier-plugin-tailwindcss": "^0.5.7",
As mentioned more above in the issue, we don't yet have a release with the issue fixed, but it is coming, so you'll have to use the patch for now. To use the patch you can either use
pnpm
's built-in patching mechanism, or use something like patch-package.If you prefer using pnpm, you will need to go through the following:
- add the following to the end of your
package.json
"pnpm": { "patchedDependencies": { "@react-email/tailwind@0.0.12": "patches/@react-email__tailwind@0.0.12.patch" } }
- copy and paste the following inside of
patches/@react-email__tailwind@0.0.12.patch
diff --git a/dist/index.js b/dist/index.js index 12a4d02aac2b2364deb79d7785bb5b2d735d9e12..12a042dd383a3e91cbfe803af11210c5b40457a7 100644 --- a/dist/index.js +++ b/dist/index.js @@ -154,6 +154,17 @@ function processElement(element, headStyles, twi) { ...processedChildren ); } + if (typeof modifiedElement.type === "function") { + const component = modifiedElement.type; + const renderedComponent = component(modifiedElement.props); + if (React.isValidElement(renderedComponent)) { + modifiedElement = processElement( + renderedComponent, + headStyles, + twi + ); + } + } return modifiedElement; } function processHead(child, responsiveStyles) { diff --git a/dist/index.mjs b/dist/index.mjs index 2e58d8425e6bf5ed3928e99d20fd1e1006d70e36..3f7b69f98aaf9ab4c45c27153d55b774e6e326f8 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -121,6 +121,17 @@ function processElement(element, headStyles, twi) { ...processedChildren ); } + if (typeof modifiedElement.type === "function") { + const component = modifiedElement.type; + const renderedComponent = component(modifiedElement.props); + if (React.isValidElement(renderedComponent)) { + modifiedElement = processElement( + renderedComponent, + headStyles, + twi + ); + } + } return modifiedElement; } function processHead(child, responsiveStyles) {
- Run
pnpm install
and the patch should be applied and the component should work now. Something to note that is important is that if you are not usingpnpm
somewhere the patch won't be applied, in a CI pipeline for example, so be sure to look out for that. A plus topatch-package
if you are just using normal npm.Also make sure you have
@react-email/tailwind@0.0.12
installed since the patch is made for this version. For thepatch-package
way, you can look into its docs and I believe its going to be pretty similar to the above.For context, a patch would be a manual change to a installed module that has some kind of problem that has not been fixed on a new version, its a sad reality, but sometimes we need patches on certain modules.
So instead of needing to do it manually there are these tools (such as
pnpm patch
orpatch-package
) that help you do this easily and in a way that everyone that also installs the dependencies of the project does not have to go through the library code again or apply the change manually.Let me know if this works for you
Appreciate this temporary alternative. It's not perfect but a better workaround than wrapping multiple components around <Tailwind />
Appreciate this temporary alternative. It's not perfect but a better workaround than wrapping multiple components around
<Tailwind />
Hey @OussamaFadlaoui we have a new canary with a PR that should've fixed these kinds of issues, try out @react-email/tailwind@0.0.13-canary.3
and any problems you have we will fix. Thanks for the patience!
Unfortunately this still does not work for me event with @react-email/tailwind
v0.0.13
- styles are not applied to custom components
EDIT: My components were wrapped with memo
. I removed that and now it is working - styles are passed
Unfortunately this still does not work for me event with
@react-email/tailwind
v0.0.13
- styles are not applied to custom components
me too
Hey guys @lunoob @tomaszczura, the original underlying problem here was fixed. If there is a new problem I recommend you open up a new issue because it is going to be something else that may be somewhat of related to the problem. Also, please don't forget to make a reproduction of your problem because that makes it much easier for me to help. Thanks!
Hey @gabrielmfern Is it not possible to use react hooks within the Tailwind component?
If I try using a React hook inside of my e-mail components wrapped in <Tailwind>
then I get the rules of hooks error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Minimal reproduction looks like:
const TestA = () => {
const [foo] = React.useState("hello")
return <p>{foo}</p>
}
const html = render(
<Tailwind>
<TestA />
</Tailwind>
)
The use case is to wrap a React context around the e-mail with common props and then consume them from some child components.
@chris-trait
Hey, this is expected behavior, email templates will generally not work with hooks since they are supposed to be static,
all data that you want to add anywhere on it you should be passing through props to it before rendering with @react-email/render
.
Hooks will work if you are rendering the email directly with React's rendering process but won't with our rendering process
because it generates static markup.
@gabrielmfern that does seem a little weird to me, as I do expect hooks to run for other "static" use cases like static site generation or server-side rendering, especially as async server side rendering is becoming more of a thing, and indeed hooks run fine with renderToStaticMarkup
. React contexts also work fine in static rendering, the issue is with how the tailwind
function traverses the component tree and calls components as plain functions: https://github.com/resendlabs/react-email/blob/2374d0d099360c63fe7427febb239776c3bd6b3b/packages/tailwind/src/tailwind.tsx#L95C32-L95C32.
For now my workaround is using <MyReactContext.Consumer>{context => ...}</MyReactContext.Consumer>
instead of the hook.
@gabrielmfern that does seem a little weird to me, as I do expect hooks to run for other "static" use cases like static site generation or server-side rendering, especially as async server side rendering is becoming more of a thing, and indeed hooks run fine with
renderToStaticMarkup
. React contexts also work fine in static rendering, the issue is with how thetailwind
function traverses the component tree and calls components as plain functions: https://github.com/resendlabs/react-email/blob/2374d0d099360c63fe7427febb239776c3bd6b3b/packages/tailwind/src/tailwind.tsx#L95C32-L95C32.For now my workaround is using
<MyReactContext.Consumer>{context => ...}</MyReactContext.Consumer>
instead of the hook.
That's interesting, then I think this is an actual issue with the new Tailwind version, can you open an issue on this so we can reference it on a PR and discuss it there?
Describe the Bug
Before version 0.0.8, the following code is working normally
After version 0.0.8, I found that it only analyzes Component with the Children attribute
Which package is affected (leave empty if unsure)
@react-email/tailwind
Link to the code that reproduces this issue
example in codesandbox
To Reproduce
I compared the latest version and the source code of version 0.0.8, and found that the 0.0.8 version was replaced by the entire HTML, while the latest version only analyzes Children after the version 0.0.8.
0.0.8
0.0.8+
Expected Behavior
it can be parse the whole template component
What's your node version? (if relevant)
No response