react-email icon indicating copy to clipboard operation
react-email copied to clipboard

feat(tailwind): Smaller bundle size

Open gabrielmfern opened this issue 2 years ago • 15 comments

Background

After the updates to the Tailwind component that came with performance improvements we had a lot more environments to consider while trying to run tailwindcss under the hood for generating the styles from classes.

These environment constraints, like being able to run on the browser and on other environments, forced us into having to polyfill certain node modules — that both postcss and tailwindcss use under the hood — while bundling even though these code paths were never really hit when running.

The reason these code paths were not completely tree shaken out of the built code was because we called tailwindcss directly as a plugin on postcss.

Side note: This PR was first intended for #1104 but ended up into a better implementation of #1124

Why?

Polyfilling was the temporary solution for this, but it is not optimal in the least. This caused issues like:

  • #1101
  • #1244
  • #1105

And ended up also being a burden to maintain and debug.

The solution

The best way I've found to solve these issues is by calling out the internal Tailwind functions that do what they need to do on postcss data structures. This also allows us to substitute a bit of our logic, which was previously purely done with regexes, with postcss's utilities which makes it much easier to read and maintain.

The reason I choose to call out the internal Tailwind functions was because the code that was Node specific was really just around finding the configuration or checking if a certain thing was installed, and this would really only run when it was called as a postcss plugin.

The way I do call out the internal Tailwind functions is heavily inspired by the way they do it themselves on the Tailwind VS Code extension/LSP Server.

Other changes that make this a refactor

Along with this, I also had to change up a few things. The first and biggest one was applying the non-inlinable styles — currently media queries, i.e. sm:... — after our first pass on the React tree which is for inlining styles. This is because we are now generating the styles for each element's classes on-the-fly instead of at once (which is still performant the way it is implemented), so we need to aggregate all the non-inlinable classes and their CSS to add to the <head> later.

The second change should be mostly internal, (and should also make it easier for others to contribute), which is a new mapReactTree utility that goes through the whole tree and separates the concerns about when to go recursive on the elements or not. It will also make it easier to actually look at #1104 once we get there.

Added tests for ensured stability

As I mentioned, we had to consider a bunch of different environments and testing them manually was really time-consuming, so, borrowed from #1124, I implemented an integration tested powered by yalc that allows us to make sure that the component will work well with NextJS's build process.

You can find this test inside packages/tailwind/integrations/_tests/nextjs.spec.ts. This PR also adds a test for Vite as well.

gabrielmfern avatar Mar 27 '24 16:03 gabrielmfern

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-email ✅ Ready (Inspect) Visit Preview 💬 Add feedback Sep 18, 2024 6:52pm
react-email-demo ✅ Ready (Inspect) Visit Preview 💬 Add feedback Sep 18, 2024 6:52pm

vercel[bot] avatar Mar 27 '24 16:03 vercel[bot]

Hi, is there any way we can get a patch for pnpm for this one? I would like to test whether it actually fixes one of the issues that we are not 100% sure this fixes :)

VaniaPopovic avatar Apr 17 '24 21:04 VaniaPopovic

@VaniaPopovic Since this is going to be quite a big change, I recommend you rather download the repo, build it and set the version of the @react-email/tailwind as a path to the cloned repo. So:

- "@react-email/tailwind": "0.0.15"
+ "@react-email/tailwind": "/path/to/clone/packages/tailwind"

This is going to be mostly representative of how it will run when installing it from npm. If you want something even more accurate, you can check out yalc.

Also, let me know what other kinds of issues this fixes!

gabrielmfern avatar Apr 18 '24 13:04 gabrielmfern

Hey! We would also like to try if these changes fix our issue (#1101). Is there an estimated release date for this? Thanks!

teovillanueva avatar Apr 18 '24 14:04 teovillanueva

Hey @teovillanueva, can't give an ETA for now, but you can enable notifications and once we do review and merge this you will be notified.

gabrielmfern avatar May 02 '24 16:05 gabrielmfern

Fwiw, I've been using a fork of this to get compatibility with nextJS 14.3.0.

It's been working well so far.

Xexr avatar May 09 '24 07:05 Xexr

@Xexr is it jsx-email? The issues you had were because of render or was it tailwind?

gabrielmfern avatar May 09 '24 11:05 gabrielmfern

@gabrielmfern Yes its jsx-email, but I spoke too soon.

I actually now get the error:

 TypeError: renderToStream is not a function

The original issue I had is this: https://github.com/resend/react-email/issues/1413

So I've been trying to run the tailwind package from this in the interim so that I can use React19 (via NextJS 14.3.0)

It does now build correctly, but when sending an email I now get the above error.

Edit: I believe the issue is due to me using [email protected] which appears to have a dependency of [email protected] instead of 0.0.13 as per this PR. Does that sound like the likely cause?

Xexr avatar May 09 '24 12:05 Xexr

@Xexr Hmm, thinking this might be fixed by #1443 since Next's canaries are using beta React, it also overrides the react-dom version you have installed, meaning it's basically using React 19. But, Resend will also need to upgrade right after that is released to fix it as well.

gabrielmfern avatar May 09 '24 12:05 gabrielmfern

Thanks @gabrielmfern

#1443 allowed me to progress past that error.

I merged it in to react-email render and then used that in a fork of resend with react/react-dom pegged to the 19 beta.

However it now appears to get stuck in a loop and doesn't finish rendering.

Xexr avatar May 09 '24 21:05 Xexr

However it now appears to get stuck in a loop and doesn't finish rendering.

@Xexr We do now have #1443 released on the latest @react-email/render and @react-email/components, can you make a repro with them? All the situations I tried with this change to the render still worked.

Also, are you rendering this on the browser or on the server? It uses a different react-dom function depending on the environment, so this could be a clue.

gabrielmfern avatar May 22 '24 16:05 gabrielmfern

Hi @gabrielmfern, thanks for this.

This was rendering on the server and it was resend that was stuck in a loop specifically.

I've moved to a different architecture to get around this with react18 dependent packages on a separate server pegged to v18 as this was proving a blocker.

Will try a minimal repro still though when I get some spare time 👍

Xexr avatar May 22 '24 17:05 Xexr

I'm a bit confused here (still new to resend). I made in issue here: https://github.com/resend/resend-node/issues/355 Not using tailwind. I simply installed the resend package with next canary & rc, made an action to call resend, Resend error: TypeError: renderToStream is not a function and the stacktrace shows @react-email. I'm confused about getting passed this error. What is the path foward? Fork this repo, make file changes, use the fork in our projects?

robahtou avatar Jun 03 '24 11:06 robahtou

@robahtou If you are using React 19, the issue is probably that the Resend SDK hasn't yet updated the dependency on @react-email/render. You should be able to fix this by just adding an override to @react-email/render as the latest. If you aren't using the Resend SDK, it might just be that you are using an older version of @react-email/render.

If that still doesn't cut it, you should be able to downgrade your React to 18 with a few warnings as a result while the Resend SDK doesn't fix it.

If my instructions don't work for you, your issue might be something else that what @Xexr had, and I'd appreciate you open an issue for it, so I can take a lot separately.

Also, the discussion I had with @Xexr was actually unrelated to this PR, so this PR doesn't fix your issue.

gabrielmfern avatar Jun 03 '24 16:06 gabrielmfern

Is this going to get fixed or what is going on here? Seems trivial but I'm not that tech savvy and I can't be bothered to fork 5 different repos to fix this. As far as I can tell its just 2 lines of code that should be changed. This is blocking me from starting to use React 19. Right now, using React 19 and this will always result in an error. How is this not a priority fix?

LarsSalembier avatar Jun 04 '24 23:06 LarsSalembier

🦋 Changeset detected

Latest commit: 4ec6fb346377930fd64672f562631c4354e99506

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@react-email/tailwind Minor
@react-email/components Patch
react-email-with-aws-ses Patch
react-email-with-mailersend Patch
react-email-with-nodemailer Patch
react-email-with-plunk Patch
react-email-with-postmark Patch
react-email-with-resend Patch
react-email-with-sendgrid Patch
react-email-with-next-scaleway Patch
react-email-with-node-scaleway Patch
react-email-starter Patch
create-email Patch

Not sure what this means? Click here to learn what changesets are.

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

changeset-bot[bot] avatar Sep 18 '24 15:09 changeset-bot[bot]