├── .changeset ├── README.md ├── afraid-sides-agree.md ├── config.json ├── crazy-seas-eat.md ├── deep-clowns-bet.md ├── great-parrots-yell.md ├── mighty-pigs-add.md ├── pre.json ├── puny-chicken-argue.md ├── stupid-ghosts-decide.md ├── tasty-swans-taste.md ├── tidy-geese-cross.md └── tiny-rice-give.md ├── .github ├── DISCUSSION_TEMPLATE │ ├── help.yml │ └── ideas.yml ├── ISSUE_TEMPLATE │ ├── 1.bug_report.yml │ └── config.yml ├── pull_request_template.md └── workflows │ ├── lint.yml │ ├── preview-release.yml │ ├── release-canary.yml │ ├── tests.yml │ └── version.yml ├── .gitignore ├── .npmrc ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── apps ├── demo │ ├── .gitignore │ ├── emails │ │ ├── magic-links │ │ │ ├── aws-verify-email.tsx │ │ │ ├── linear-login-code.tsx │ │ │ ├── notion-magic-link.tsx │ │ │ ├── plaid-verify-identity.tsx │ │ │ ├── raycast-magic-link.tsx │ │ │ └── slack-confirm.tsx │ │ ├── newsletters │ │ │ ├── codepen-challengers.tsx │ │ │ ├── google-play-policy-update.tsx │ │ │ └── stack-overflow-tips.tsx │ │ ├── notifications │ │ │ ├── github-access-token.tsx │ │ │ ├── papermark-year-in-review.tsx │ │ │ ├── vercel-invite-user.tsx │ │ │ └── yelp-recent-login.tsx │ │ ├── receipts │ │ │ ├── apple-receipt.tsx │ │ │ └── nike-receipt.tsx │ │ ├── reset-password │ │ │ ├── dropbox-reset-password.tsx │ │ │ └── twitch-reset-password.tsx │ │ ├── reviews │ │ │ ├── airbnb-review.tsx │ │ │ └── amazon-review.tsx │ │ ├── static │ │ │ ├── airbnb-logo.png │ │ │ ├── airbnb-review-user.jpg │ │ │ ├── amazon-book.jpg │ │ │ ├── amazon-facebook.jpg │ │ │ ├── amazon-instagram.jpg │ │ │ ├── amazon-logo.png │ │ │ ├── amazon-prime-logo.png │ │ │ ├── amazon-rating.gif │ │ │ ├── amazon-twitter.jpg │ │ │ ├── apple-card-icon.png │ │ │ ├── apple-hbo-max-icon.jpeg │ │ │ ├── apple-logo.png │ │ │ ├── apple-wallet.png │ │ │ ├── aws-logo.png │ │ │ ├── codepen-challengers.png │ │ │ ├── codepen-cube.png │ │ │ ├── codepen-pro.png │ │ │ ├── dropbox-logo.png │ │ │ ├── github.png │ │ │ ├── google-play-academy.png │ │ │ ├── google-play-chat.png │ │ │ ├── google-play-footer.png │ │ │ ├── google-play-header.png │ │ │ ├── google-play-icon.png │ │ │ ├── google-play-logo.png │ │ │ ├── google-play-pl.png │ │ │ ├── google-play.png │ │ │ ├── koala-logo.png │ │ │ ├── linear-logo.png │ │ │ ├── netlify-logo.png │ │ │ ├── nike-logo.png │ │ │ ├── nike-phone.png │ │ │ ├── nike-product.png │ │ │ ├── nike-recomendation-1.png │ │ │ ├── nike-recomendation-2.png │ │ │ ├── nike-recomendation-3.png │ │ │ ├── nike-recomendation-4.png │ │ │ ├── notion-logo.png │ │ │ ├── plaid-logo.png │ │ │ ├── raycast-bg.png │ │ │ ├── raycast-logo.png │ │ │ ├── slack-facebook.png │ │ │ ├── slack-linkedin.png │ │ │ ├── slack-logo.png │ │ │ ├── slack-twitter.png │ │ │ ├── stack-overflow-header.png │ │ │ ├── stack-overflow-logo-sm.png │ │ │ ├── stack-overflow-logo.png │ │ │ ├── stripe-logo.png │ │ │ ├── twitch-icon-facebook.png │ │ │ ├── twitch-icon-twitter.png │ │ │ ├── twitch-logo.png │ │ │ ├── vercel-arrow.png │ │ │ ├── vercel-logo.png │ │ │ ├── vercel-team.png │ │ │ ├── vercel-user.png │ │ │ ├── yelp-footer.png │ │ │ ├── yelp-header.png │ │ │ └── yelp-logo.png │ │ └── welcome │ │ │ ├── koala-welcome.tsx │ │ │ ├── netlify-welcome.tsx │ │ │ └── stripe-welcome.tsx │ ├── license.md │ └── package.json ├── docs │ ├── changelog.mdx │ ├── cli.mdx │ ├── components │ │ ├── button.mdx │ │ ├── code-block.mdx │ │ ├── code-inline.mdx │ │ ├── column.mdx │ │ ├── container.mdx │ │ ├── font.mdx │ │ ├── head.mdx │ │ ├── heading.mdx │ │ ├── hr.mdx │ │ ├── html.mdx │ │ ├── image.mdx │ │ ├── link.mdx │ │ ├── markdown.mdx │ │ ├── preview.mdx │ │ ├── row.mdx │ │ ├── section.mdx │ │ ├── tailwind.mdx │ │ └── text.mdx │ ├── contributing.mdx │ ├── contributing │ │ ├── codebase-overview.mdx │ │ ├── development-workflow │ │ │ ├── 1-setup.mdx │ │ │ ├── 2-running-tests.mdx │ │ │ ├── 3-linting.mdx │ │ │ ├── 4-building.mdx │ │ │ └── 5-writing-docs.mdx │ │ ├── introduction.mdx │ │ ├── opening-issues.mdx │ │ └── opening-pull-requests.mdx │ ├── deployment.mdx │ ├── favicon.png │ ├── getting-started │ │ ├── automatic-setup.mdx │ │ ├── manual-setup.mdx │ │ ├── migrating-to-react-email.mdx │ │ └── monorepo-setup │ │ │ ├── bun.mdx │ │ │ ├── choose-package-manager.mdx │ │ │ ├── npm.mdx │ │ │ ├── pnpm.mdx │ │ │ └── yarn.mdx │ ├── images │ │ ├── background.png │ │ ├── bg.png │ │ ├── local-dev.jpg │ │ └── preview-server-vercel-settings.png │ ├── integrations │ │ ├── aws-ses.mdx │ │ ├── mailersend.mdx │ │ ├── nodemailer.mdx │ │ ├── overview.mdx │ │ ├── plunk.mdx │ │ ├── postmark.mdx │ │ ├── resend.mdx │ │ ├── scaleway.mdx │ │ └── sendgrid.mdx │ ├── introduction.mdx │ ├── license.md │ ├── logo │ │ ├── dark.svg │ │ └── light.svg │ ├── mint.json │ ├── package.json │ ├── roadmap.mdx │ ├── snippets │ │ ├── integrations.mdx │ │ ├── localdev.mdx │ │ ├── next-steps.mdx │ │ └── support.mdx │ └── utilities │ │ └── render.mdx └── web │ ├── .gitignore │ ├── .vscode │ └── settings.json │ ├── README.md │ ├── components │ ├── _components │ │ └── layout.tsx │ ├── article-with-image-as-background │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── article-with-image-on-right │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── article-with-image │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── article-with-multiple-authors │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── article-with-single-author │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── article-with-two-cards │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── bento-grid │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── checkout │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── code-block-with-custom-theme │ │ └── index.tsx │ ├── code-block-with-line-numbers │ │ └── index.tsx │ ├── code-block-with-predefined-theme │ │ └── index.tsx │ ├── code-block-without-theme │ │ └── index.tsx │ ├── code-inline-with-different-colors │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── customer-reviews │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── divider-between-rows-and-columns │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── download-buttons │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── ensure-matching-variants.spec.tsx │ ├── footer-with-one-column │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── footer-with-two-columns │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── four-images-in-a-grid │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-and-four-paragraphs-and-two-columns │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-and-four-paragraphs │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-and-list-items │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-and-numbered-list-items │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-and-three-centered-paragraphs │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-with-centered-menu │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-with-side-menu │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── header-with-social-icons │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── image-with-varying-sizes │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── images-on-horizontal-grid │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── images-on-vertical-grid │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── link-inline-with-text │ │ └── index.tsx │ ├── list-with-image-on-left │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── markdown-with-container-styles │ │ └── index.tsx │ ├── markdown-with-custom-styles │ │ └── index.tsx │ ├── multiple-headings │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── one-product-with-image-on-the-left │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── one-product │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── one-row-three-columns │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── one-row-two-columns │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── rounded-image │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── section-with-rows-and-columns │ │ └── index.tsx │ ├── simple-code-inline │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-container │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-divider │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-heading │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-image │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-link │ │ └── index.tsx │ ├── simple-list │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-markdown │ │ └── index.tsx │ ├── simple-pricing-table │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-rating-survey │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── simple-section │ │ └── index.tsx │ ├── simple-text │ │ └── index.tsx │ ├── single-button │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── static │ │ ├── cube-icon.png │ │ ├── download-on-the-app-store.png │ │ ├── facebook-logo.png │ │ ├── get-it-on-google-play.png │ │ ├── heart-icon.png │ │ ├── in-icon.png │ │ ├── instagram-logo.png │ │ ├── logo-without-background.png │ │ ├── megaphone-icon.png │ │ ├── rocket-icon.png │ │ ├── steve-jobs.jpg │ │ ├── steve-wozniak.jpg │ │ ├── x-icon.png │ │ └── x-logo.png │ ├── structure.ts │ ├── survey-section │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── tailwind.config.ts │ ├── text-with-styling │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── three-columns-with-images │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── title-four-cards │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── title-three-cards-in-a-row │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── tsconfig.json │ ├── two-buttons │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ └── two-tiers-with-emphasized-tier │ │ ├── inline-styles.tsx │ │ └── tailwind.tsx │ ├── license.md │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── brand │ │ ├── logo-without-background.png │ │ ├── logo.png │ │ └── resend.png │ ├── examples │ │ ├── airbnb-review.png │ │ ├── apple-receipt.png │ │ ├── authors │ │ │ ├── EmersonGarrido.png │ │ │ ├── Rychillie.png │ │ │ ├── abhinandanwadwa.png │ │ │ ├── bruno88cabral.png │ │ │ ├── bukinoshita.png │ │ │ ├── c0dr.png │ │ │ ├── camillegachido.png │ │ │ ├── joaom00.png │ │ │ ├── nettofarah.png │ │ │ ├── relferreira.png │ │ │ ├── ribeiroevandro.png │ │ │ ├── thecodeinfluencer.png │ │ │ └── zenorocha.png │ │ ├── aws-verify-email.png │ │ ├── dropbox-reset-password.png │ │ ├── github-access-token.png │ │ ├── google-play-policy-update.png │ │ ├── koala-welcome.png │ │ ├── linear-login-code.png │ │ ├── nike-receipt.png │ │ ├── notion-magic-link.png │ │ ├── plaid-verify-identity.png │ │ ├── raycast-magic-link.png │ │ ├── slack-confirm.png │ │ ├── stack-overflow-tips.png │ │ ├── stripe-welcome.png │ │ ├── twitch-reset-password.png │ │ ├── vercel-invite-user.png │ │ └── yelp-recent-login.png │ ├── fonts │ │ ├── commit-mono │ │ │ ├── commit-mono-italic.ttf │ │ │ └── commit-mono-regular.ttf │ │ ├── inter │ │ │ └── inter.ttf │ │ └── shantell-sans │ │ │ ├── shantell-sans-italic.ttf │ │ │ └── shantell-sans-regular.ttf │ ├── meta │ │ ├── apple-touch-icon.png │ │ ├── cover.png │ │ ├── favicon.ico │ │ └── favicon.svg │ └── static │ │ ├── atmos-vacuum-canister.jpg │ │ ├── bg.png │ │ ├── braun-analogue-clock.jpg │ │ ├── braun-classic-watch.jpg │ │ ├── braun-collection.jpg │ │ ├── braun-vintage.jpg │ │ ├── braun-wall-clock.jpg │ │ ├── braun-wireless-alarm.jpg │ │ ├── bundle-collection.jpg │ │ ├── clara-french-press.jpg │ │ ├── clyde-electric-kettle.jpg │ │ ├── coffee-bean-storage.jpg │ │ ├── covers │ │ ├── button.png │ │ ├── code-block.png │ │ ├── code-inline.png │ │ ├── column.png │ │ ├── components.png │ │ ├── container.png │ │ ├── create-email.png │ │ ├── font.png │ │ ├── head.png │ │ ├── heading.png │ │ ├── hr.png │ │ ├── html.png │ │ ├── img.png │ │ ├── link.png │ │ ├── markdown.png │ │ ├── patterns.png │ │ ├── preview.png │ │ ├── react-email.png │ │ ├── render.png │ │ ├── row.png │ │ ├── section.png │ │ ├── tailwind.png │ │ └── text.png │ │ ├── cube-icon.png │ │ ├── download-on-the-app-store.png │ │ ├── facebook-logo.png │ │ ├── get-it-on-google-play.png │ │ ├── grinder-collection.jpg │ │ ├── heart-icon.png │ │ ├── herman-miller-chair.jpg │ │ ├── icons │ │ ├── apple-mail.svg │ │ ├── gmail.svg │ │ ├── hey.svg │ │ ├── outlook.svg │ │ ├── superhuman.svg │ │ └── yahoo-mail.svg │ │ ├── in-icon.png │ │ ├── instagram-logo.png │ │ ├── logo-without-background.png │ │ ├── megaphone-icon.png │ │ ├── monty-art-cup-1.jpg │ │ ├── monty-art-cup-2.jpg │ │ ├── mugs-collection.jpg │ │ ├── ode-grinder.jpg │ │ ├── outdoor-living.jpg │ │ ├── rocket-icon.png │ │ ├── stagg-eletric-kettle.jpg │ │ ├── steve-jobs.jpg │ │ ├── steve-wozniak.jpg │ │ ├── vacuum-canister-clear-glass-bundle.jpg │ │ ├── versatile-comfort.jpg │ │ ├── x-icon.png │ │ └── x-logo.png │ ├── src │ ├── app │ │ ├── api │ │ │ ├── check-spam │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── check-spam.spec.tsx.snap │ │ │ │ ├── check-spam.spec.tsx │ │ │ │ ├── check-spam.ts │ │ │ │ ├── route.ts │ │ │ │ └── testing │ │ │ │ │ └── stripe-welcome-email.tsx │ │ │ └── send │ │ │ │ └── test │ │ │ │ └── route.ts │ │ ├── components │ │ │ ├── [slug] │ │ │ │ └── page.tsx │ │ │ ├── get-imported-components-for.tsx │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── not-found.tsx │ │ ├── page.tsx │ │ ├── robots.ts │ │ ├── sitemap.ts │ │ └── templates │ │ │ └── page.tsx │ ├── components │ │ ├── anchor.tsx │ │ ├── button.tsx │ │ ├── code-block.tsx │ │ ├── code.tsx │ │ ├── component-code-view.tsx │ │ ├── component-preview.tsx │ │ ├── component-view.tsx │ │ ├── components-view.tsx │ │ ├── footer.tsx │ │ ├── heading.tsx │ │ ├── icon-button.tsx │ │ ├── icons │ │ │ ├── icon-arrow-left.tsx │ │ │ ├── icon-base.tsx │ │ │ ├── icon-monitor.tsx │ │ │ ├── icon-phone.tsx │ │ │ └── icon-source.tsx │ │ ├── logo.tsx │ │ ├── menu.tsx │ │ ├── page-transition.tsx │ │ ├── spotlight.tsx │ │ ├── tab-trigger.tsx │ │ ├── template.tsx │ │ ├── text.tsx │ │ ├── tooltip-content.tsx │ │ ├── tooltip.tsx │ │ └── topbar.tsx │ ├── hooks │ │ └── use-stored-state.ts │ ├── illustrations │ │ ├── articles.tsx │ │ ├── buttons.tsx │ │ ├── code-block.tsx │ │ ├── code-inline.tsx │ │ ├── container.tsx │ │ ├── divider.tsx │ │ ├── ecommerce.tsx │ │ ├── features.tsx │ │ ├── feedback.tsx │ │ ├── footers.tsx │ │ ├── gallery.tsx │ │ ├── grid.tsx │ │ ├── headers.tsx │ │ ├── heading.tsx │ │ ├── image.tsx │ │ ├── link.tsx │ │ ├── list.tsx │ │ ├── markdown.tsx │ │ ├── marketing.tsx │ │ ├── pricing.tsx │ │ ├── section.tsx │ │ └── text.tsx │ ├── styles │ │ └── globals.css │ └── utils │ │ ├── as.ts │ │ ├── convert-uris-into-urls.spec.ts │ │ ├── convert-uris-into-urls.ts │ │ ├── slugify.ts │ │ ├── spam-assassin │ │ ├── __snapshots__ │ │ │ └── parse-pointing-table-rows.spec.ts.snap │ │ ├── parse-pointing-table-rows.spec.ts │ │ ├── parse-pointing-table-rows.ts │ │ └── send-to-spamd.ts │ │ └── unreachable.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ └── vitest.config.ts ├── benchmarks ├── preview-server │ ├── bench-results-30-iterations.json │ ├── package.json │ ├── src │ │ ├── local-vs-2.1.7-canary.2-on-startup.ts │ │ ├── local-vs-2.1.7-canary.2.ts │ │ └── utils │ │ │ ├── run-server-and-fetch-preview-page.ts │ │ │ └── run-server.ts │ └── startup-bench-results-30-iterations.json └── tailwind-component │ ├── .gitignore │ ├── README.md │ ├── bench-results-100-iterations.json │ ├── package.json │ ├── src │ ├── benchmark-0.0.12-vs-local-version.tsx │ ├── benchmark-0.0.17-vs-local-version.tsx │ ├── benchmark-with-vs-without.tsx │ ├── emails │ │ ├── with-tailwind.tsx │ │ └── without-tailwind.tsx │ └── tailwind-render.tsx │ ├── tailwind.config.js │ └── tsconfig.json ├── biome.json ├── examples ├── aws-ses │ ├── package.json │ ├── src │ │ ├── email.tsx │ │ └── index.tsx │ └── tsconfig.json ├── mailersend │ ├── package.json │ ├── src │ │ ├── email.tsx │ │ └── index.tsx │ └── tsconfig.json ├── nodemailer │ ├── package.json │ ├── src │ │ ├── email.tsx │ │ └── index.tsx │ └── tsconfig.json ├── plunk │ ├── package.json │ ├── src │ │ ├── email.tsx │ │ └── index.tsx │ └── tsconfig.json ├── postmark │ ├── package.json │ ├── src │ │ ├── email.tsx │ │ └── index.tsx │ └── tsconfig.json ├── resend │ ├── next-env.d.ts │ ├── package.json │ ├── src │ │ ├── lib │ │ │ └── resend.ts │ │ └── pages │ │ │ └── api │ │ │ └── send.ts │ ├── transactional │ │ └── emails │ │ │ └── waitlist.tsx │ └── tsconfig.json ├── scaleway │ ├── next │ │ ├── next-env.d.ts │ │ ├── package.json │ │ ├── src │ │ │ ├── lib │ │ │ │ └── scaleway.ts │ │ │ └── pages │ │ │ │ └── api │ │ │ │ └── send.tsx │ │ ├── transactional │ │ │ └── emails │ │ │ │ └── waitlist.tsx │ │ └── tsconfig.json │ └── node │ │ ├── package.json │ │ ├── src │ │ ├── email.tsx │ │ └── index.tsx │ │ └── tsconfig.json └── sendgrid │ ├── package.json │ ├── src │ ├── email.tsx │ └── index.tsx │ └── tsconfig.json ├── package.json ├── packages ├── body │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── body.spec.tsx.snap │ │ ├── body.spec.tsx │ │ ├── body.tsx │ │ └── index.ts │ └── tsconfig.json ├── button │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── button.spec.tsx.snap │ │ ├── button.spec.tsx │ │ ├── button.tsx │ │ ├── index.ts │ │ └── utils │ │ │ ├── parse-padding.ts │ │ │ ├── px-to-pt.ts │ │ │ └── utils.spec.ts │ └── tsconfig.json ├── code-block │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── code-block.tsx │ │ ├── index.ts │ │ ├── languages-available.ts │ │ ├── prism.ts │ │ └── themes.ts │ └── tsconfig.json ├── code-inline │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── code-inline.tsx │ │ └── index.ts │ └── tsconfig.json ├── column │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── column.spec.tsx.snap │ │ ├── column.spec.tsx │ │ ├── column.tsx │ │ └── index.ts │ └── tsconfig.json ├── components │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── heading.spec.tsx.snap │ │ └── index.ts │ └── tsconfig.json ├── container │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── container.spec.tsx.snap │ │ ├── container.spec.tsx │ │ ├── container.tsx │ │ └── index.ts │ └── tsconfig.json ├── create-email │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── index.js │ │ ├── index.spec.ts │ │ └── tree.js │ ├── template │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── emails │ │ │ ├── notion-magic-link.tsx │ │ │ ├── plaid-verify-identity.tsx │ │ │ ├── static │ │ │ │ ├── notion-logo.png │ │ │ │ ├── plaid-logo.png │ │ │ │ ├── plaid.png │ │ │ │ ├── stripe-logo.png │ │ │ │ ├── vercel-arrow.png │ │ │ │ ├── vercel-logo.png │ │ │ │ ├── vercel-team.png │ │ │ │ └── vercel-user.png │ │ │ ├── stripe-welcome.tsx │ │ │ └── vercel-invite-user.tsx │ │ ├── package.json │ │ ├── readme.md │ │ └── tsconfig.json │ ├── tsconfig.json │ └── vitest.config.ts ├── font │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── font.spec.tsx.snap │ │ ├── font.spec.tsx │ │ ├── font.tsx │ │ └── index.ts │ └── tsconfig.json ├── head │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── head.spec.tsx.snap │ │ ├── head.spec.tsx │ │ ├── head.tsx │ │ └── index.ts │ └── tsconfig.json ├── heading │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── heading.spec.tsx.snap │ │ ├── heading.spec.tsx │ │ ├── heading.tsx │ │ ├── index.ts │ │ └── utils │ │ │ ├── as.ts │ │ │ ├── spaces.ts │ │ │ └── utils.spec.ts │ └── tsconfig.json ├── hr │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── hr.spec.tsx.snap │ │ ├── hr.spec.tsx │ │ ├── hr.tsx │ │ └── index.ts │ └── tsconfig.json ├── html │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── html.spec.tsx.snap │ │ ├── html.spec.tsx │ │ ├── html.tsx │ │ └── index.ts │ └── tsconfig.json ├── img │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── img.spec.tsx.snap │ │ ├── img.spec.tsx │ │ ├── img.tsx │ │ └── index.ts │ └── tsconfig.json ├── link │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── link.spec.tsx.snap │ │ ├── index.ts │ │ ├── link.spec.tsx │ │ └── link.tsx │ └── tsconfig.json ├── markdown │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── markdown.spec.tsx.snap │ │ ├── index.ts │ │ ├── markdown.spec.tsx │ │ └── markdown.tsx │ └── tsconfig.json ├── preview-server │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── _index.js │ ├── license.md │ ├── module-punycode.d.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── scripts │ │ ├── build-preview-server.mjs │ │ └── fill-caniemail-data.mjs │ ├── src │ │ ├── actions │ │ │ ├── email-validation │ │ │ │ ├── caniemail-data.ts │ │ │ │ ├── check-compatibility.ts │ │ │ │ ├── check-images.spec.tsx │ │ │ │ ├── check-images.ts │ │ │ │ ├── check-links.spec.tsx │ │ │ │ ├── check-links.ts │ │ │ │ ├── get-code-location-from-ast-element.ts │ │ │ │ └── quick-fetch.ts │ │ │ ├── get-email-path-from-slug.ts │ │ │ ├── get-emails-directory-metadata-action.ts │ │ │ └── render-email-by-path.tsx │ │ ├── animated-icons-data │ │ │ ├── help.json │ │ │ ├── link.json │ │ │ ├── load.json │ │ │ └── mail.json │ │ ├── app │ │ │ ├── env.ts │ │ │ ├── favicon.ico │ │ │ ├── fonts.ts │ │ │ ├── fonts │ │ │ │ └── SFMono │ │ │ │ │ ├── SFMonoBold.otf │ │ │ │ │ ├── SFMonoBoldItalic.otf │ │ │ │ │ ├── SFMonoHeavy.otf │ │ │ │ │ ├── SFMonoHeavyItalic.otf │ │ │ │ │ ├── SFMonoLight.otf │ │ │ │ │ ├── SFMonoLightItalic.otf │ │ │ │ │ ├── SFMonoMedium.otf │ │ │ │ │ ├── SFMonoMediumItalic.otf │ │ │ │ │ ├── SFMonoRegular.otf │ │ │ │ │ ├── SFMonoRegularItalic.otf │ │ │ │ │ ├── SFMonoSemibold.otf │ │ │ │ │ └── SFMonoSemiboldItalic.otf │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ ├── logo.png │ │ │ ├── page.tsx │ │ │ └── preview │ │ │ │ └── [...slug] │ │ │ │ ├── page.tsx │ │ │ │ ├── preview.tsx │ │ │ │ └── rendering-error.tsx │ │ ├── components │ │ │ ├── button.tsx │ │ │ ├── code-container.tsx │ │ │ ├── code-snippet.tsx │ │ │ ├── code.tsx │ │ │ ├── heading.tsx │ │ │ ├── icons │ │ │ │ ├── icon-arrow-down.tsx │ │ │ │ ├── icon-base.tsx │ │ │ │ ├── icon-bug.tsx │ │ │ │ ├── icon-button.tsx │ │ │ │ ├── icon-check.tsx │ │ │ │ ├── icon-clipboard.tsx │ │ │ │ ├── icon-download.tsx │ │ │ │ ├── icon-email.tsx │ │ │ │ ├── icon-file.tsx │ │ │ │ ├── icon-folder-open.tsx │ │ │ │ ├── icon-folder.tsx │ │ │ │ ├── icon-hide-sidebar.tsx │ │ │ │ ├── icon-image.tsx │ │ │ │ ├── icon-info.tsx │ │ │ │ ├── icon-link.tsx │ │ │ │ ├── icon-monitor.tsx │ │ │ │ ├── icon-phone.tsx │ │ │ │ ├── icon-reload.tsx │ │ │ │ ├── icon-source.tsx │ │ │ │ ├── icon-stamp.tsx │ │ │ │ └── icon-warning.tsx │ │ │ ├── index.ts │ │ │ ├── logo.tsx │ │ │ ├── resizable-wrapper.tsx │ │ │ ├── send.tsx │ │ │ ├── shell.tsx │ │ │ ├── sidebar │ │ │ │ ├── file-tree-directory-children.tsx │ │ │ │ ├── file-tree-directory.tsx │ │ │ │ ├── file-tree.tsx │ │ │ │ ├── index.ts │ │ │ │ └── sidebar.tsx │ │ │ ├── text.tsx │ │ │ ├── toolbar.tsx │ │ │ ├── toolbar │ │ │ │ ├── checking-results.tsx │ │ │ │ ├── code-preview-line-link.tsx │ │ │ │ ├── compatibility.tsx │ │ │ │ ├── linter.tsx │ │ │ │ ├── results-table.tsx │ │ │ │ ├── results.tsx │ │ │ │ ├── spam-assassin.tsx │ │ │ │ ├── toolbar-button.tsx │ │ │ │ └── use-cached-state.ts │ │ │ ├── tooltip-content.tsx │ │ │ ├── tooltip.tsx │ │ │ ├── topbar.tsx │ │ │ └── topbar │ │ │ │ ├── active-view-toggle-group.tsx │ │ │ │ └── view-size-controls.tsx │ │ ├── contexts │ │ │ ├── emails.tsx │ │ │ ├── fragment-identifier.tsx │ │ │ └── preview.tsx │ │ ├── hooks │ │ │ ├── use-clamped-state.ts │ │ │ ├── use-email-rendering-result.ts │ │ │ ├── use-fragment-identifier.ts │ │ │ ├── use-hot-reload.ts │ │ │ ├── use-icon-animation.ts │ │ │ └── use-rendering-metadata.ts │ │ └── utils │ │ │ ├── __snapshots__ │ │ │ └── get-email-component.spec.ts.snap │ │ │ ├── caniemail │ │ │ ├── all-css-properties.ts │ │ │ ├── ast │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── get-object-variables.spec.ts.snap │ │ │ │ │ └── get-used-style-properties.spec.ts.snap │ │ │ │ ├── get-object-variables.spec.ts │ │ │ │ ├── get-object-variables.ts │ │ │ │ ├── get-used-style-properties.spec.ts │ │ │ │ └── get-used-style-properties.ts │ │ │ ├── get-compatibility-stats-for-entry.ts │ │ │ ├── get-css-functions.ts │ │ │ ├── get-css-property-names.ts │ │ │ ├── get-css-property-with-value.ts │ │ │ ├── get-css-unit.ts │ │ │ ├── get-element-attributes.ts │ │ │ ├── get-element-names.ts │ │ │ └── tailwind │ │ │ │ ├── generate-tailwind-rules.ts │ │ │ │ ├── get-tailwind-config.ts │ │ │ │ ├── get-tailwind-metadata.spec.ts │ │ │ │ ├── get-tailwind-metadata.ts │ │ │ │ └── setup-tailwind-context.ts │ │ │ ├── cn.ts │ │ │ ├── constants.ts │ │ │ ├── contains-email-template.spec.ts │ │ │ ├── contains-email-template.ts │ │ │ ├── copy-text-to-clipboard.ts │ │ │ ├── esbuild │ │ │ ├── escape-string-for-regex.ts │ │ │ └── renderring-utilities-exporter.ts │ │ │ ├── get-email-component.spec.ts │ │ │ ├── get-email-component.ts │ │ │ ├── get-emails-directory-metadata.spec.ts │ │ │ ├── get-emails-directory-metadata.ts │ │ │ ├── get-line-and-column-from-offset.spec.ts │ │ │ ├── get-line-and-column-from-offset.ts │ │ │ ├── improve-error-with-sourcemap.ts │ │ │ ├── index.ts │ │ │ ├── js-email-detection.spec.ts │ │ │ ├── language-map.ts │ │ │ ├── linting.ts │ │ │ ├── load-stream.ts │ │ │ ├── register-spinner-autostopping.ts │ │ │ ├── result.ts │ │ │ ├── run-bundled-code.ts │ │ │ ├── sanitize.ts │ │ │ ├── static-node-modules-for-vm.ts │ │ │ ├── testing │ │ │ ├── js-email-export-default.js │ │ │ ├── js-email-test.js │ │ │ ├── mdx-email-test.js │ │ │ └── request-response-email.tsx │ │ │ ├── types │ │ │ ├── as.ts │ │ │ ├── email-template.ts │ │ │ ├── error-object.ts │ │ │ └── hot-reload-change.ts │ │ │ └── unreachable.ts │ ├── tailwind-internals.d.ts │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── preview │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── preview.spec.tsx.snap │ │ ├── index.ts │ │ ├── preview.spec.tsx │ │ └── preview.tsx │ └── tsconfig.json ├── react-email │ ├── .gitignore │ ├── .npmignore │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── commands │ │ │ ├── .npmignore │ │ │ ├── build.ts │ │ │ ├── dev.ts │ │ │ ├── export.ts │ │ │ ├── start.ts │ │ │ └── testing │ │ │ │ ├── .gitignore │ │ │ │ ├── __snapshots__ │ │ │ │ └── export.spec.ts.snap │ │ │ │ └── export.spec.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── __snapshots__ │ │ │ └── tree.spec.ts.snap │ │ │ ├── esbuild │ │ │ ├── escape-string-for-regex.ts │ │ │ └── renderring-utilities-exporter.ts │ │ │ ├── get-emails-directory-metadata.spec.ts │ │ │ ├── get-emails-directory-metadata.ts │ │ │ ├── get-preview-server-location.ts │ │ │ ├── index.ts │ │ │ ├── packageJson.ts │ │ │ ├── preview │ │ │ ├── get-env-variables-for-preview-app.ts │ │ │ ├── hot-reloading │ │ │ │ ├── create-dependency-graph.spec.ts │ │ │ │ ├── create-dependency-graph.ts │ │ │ │ ├── get-imported-modules.spec.ts │ │ │ │ ├── get-imported-modules.ts │ │ │ │ ├── resolve-path-aliases.spec.ts │ │ │ │ ├── resolve-path-aliases.ts │ │ │ │ ├── setup-hot-reloading.ts │ │ │ │ └── test │ │ │ │ │ ├── some-file.ts │ │ │ │ │ └── tsconfig.json │ │ │ ├── index.ts │ │ │ ├── serve-static-file.ts │ │ │ └── start-dev-server.ts │ │ │ ├── register-spinner-autostopping.ts │ │ │ ├── tree.spec.ts │ │ │ ├── tree.ts │ │ │ └── types │ │ │ ├── hot-reload-change.ts │ │ │ └── hot-reload-event.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts ├── render │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── browser │ │ │ ├── __snapshots__ │ │ │ │ └── render-web.spec.tsx.snap │ │ │ ├── index.ts │ │ │ ├── read-stream.ts │ │ │ ├── render-web.spec.tsx │ │ │ └── render.tsx │ │ ├── node │ │ │ ├── __snapshots__ │ │ │ │ ├── render-async-edge.spec.tsx.snap │ │ │ │ ├── render-async-node.spec.tsx.snap │ │ │ │ ├── render-edge.spec.tsx.snap │ │ │ │ └── render-node.spec.tsx.snap │ │ │ ├── index.ts │ │ │ ├── read-stream.ts │ │ │ ├── render-edge.spec.tsx │ │ │ ├── render-node.spec.tsx │ │ │ └── render.tsx │ │ ├── react-internals.d.ts │ │ └── shared │ │ │ ├── options.ts │ │ │ ├── plain-text-selectors.ts │ │ │ └── utils │ │ │ ├── __snapshots__ │ │ │ └── pretty.spec.ts.snap │ │ │ ├── pretty.spec.ts │ │ │ ├── pretty.ts │ │ │ ├── preview.tsx │ │ │ ├── stripe-email.html │ │ │ └── template.tsx │ ├── tsconfig.json │ └── tsup.config.ts ├── row │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── row.spec.tsx.snap │ │ ├── index.ts │ │ ├── row.spec.tsx │ │ └── row.tsx │ └── tsconfig.json ├── section │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── section.spec.tsx.snap │ │ ├── index.ts │ │ ├── section.spec.tsx │ │ └── section.tsx │ └── tsconfig.json ├── tailwind │ ├── CHANGELOG.md │ ├── copy-tailwind-types.mjs │ ├── integrations │ │ ├── integrations.spec.ts │ │ ├── nextjs │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── emails │ │ │ │ └── vercel-invite-user.tsx │ │ │ ├── next-env.d.ts │ │ │ ├── next.config.mjs │ │ │ ├── package.json │ │ │ ├── src │ │ │ │ └── app │ │ │ │ │ ├── favicon.ico │ │ │ │ │ ├── layout.tsx │ │ │ │ │ └── page.tsx │ │ │ └── tsconfig.json │ │ └── vite │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── emails │ │ │ └── vercel-invite-user.tsx │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── public │ │ │ └── vite.svg │ │ │ ├── src │ │ │ ├── App.tsx │ │ │ ├── main.tsx │ │ │ └── vite-env.d.ts │ │ │ ├── tsconfig.json │ │ │ ├── tsconfig.node.json │ │ │ └── vite.config.ts │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── tailwind.spec.tsx.snap │ │ ├── hooks │ │ │ ├── __snapshots__ │ │ │ │ └── use-tailwind.spec.ts.snap │ │ │ └── use-suspensed-promise.ts │ │ ├── index.ts │ │ ├── tailwind.spec.tsx │ │ ├── tailwind.tsx │ │ └── utils │ │ │ ├── __snapshots__ │ │ │ └── quick-safe-render-to-string.spec.tsx.snap │ │ │ ├── compatibility │ │ │ ├── convert-css-property-to-react-property.ts │ │ │ ├── escape-class-name.spec.ts │ │ │ ├── escape-class-name.ts │ │ │ ├── sanitize-class-name.spec.ts │ │ │ ├── sanitize-class-name.ts │ │ │ └── unescape-class.ts │ │ │ ├── css │ │ │ ├── make-inline-styles-for.spec.ts │ │ │ ├── make-inline-styles-for.ts │ │ │ ├── minify-css.ts │ │ │ ├── remove-if-empty-recursively.ts │ │ │ ├── remove-rule-duplicates-from-root.ts │ │ │ ├── resolve-all-css-variables.spec.ts │ │ │ ├── resolve-all-css-variables.ts │ │ │ ├── sanitize-declarations.ts │ │ │ ├── sanitize-non-inlinable-classes.spec.ts │ │ │ └── sanitize-non-inlinable-classes.ts │ │ │ ├── react │ │ │ ├── is-component.ts │ │ │ ├── map-react-tree.spec.tsx │ │ │ └── map-react-tree.ts │ │ │ ├── tailwindcss │ │ │ ├── __snapshots__ │ │ │ │ └── setup-tailwind.spec.ts.snap │ │ │ ├── clone-element-with-inlined-styles.ts │ │ │ ├── setup-tailwind-context.ts │ │ │ ├── setup-tailwind.spec.ts │ │ │ ├── setup-tailwind.ts │ │ │ └── tailwind-internals.d.ts │ │ │ └── text │ │ │ └── from-dash-case-to-camel-case.ts │ ├── tsconfig.json │ ├── vite.config.ts │ └── vitest.config.ts ├── text │ ├── CHANGELOG.md │ ├── license.md │ ├── package.json │ ├── readme.md │ ├── src │ │ ├── __snapshots__ │ │ │ └── text.spec.tsx.snap │ │ ├── index.ts │ │ ├── text.spec.tsx │ │ ├── text.tsx │ │ └── utils │ │ │ ├── compute-margins.spec.ts │ │ │ └── compute-margins.ts │ └── tsconfig.json └── tsconfig │ ├── base.json │ ├── nextjs.json │ ├── package.json │ └── react-library.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── renovate.json ├── scripts └── check-dependency-versions.ts ├── tsconfig.json ├── turbo.json └── vitest.config.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/afraid-sides-agree.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@react-email/preview-server": patch 3 | --- 4 | 5 | fix `` not being flagged as incompatible 6 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "linked": [], 6 | "access": "public", 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "patch", 9 | "ignore": [ 10 | "@benchmarks/preview-server", 11 | "@benchmarks/tailwind-component", 12 | "demo", 13 | "web" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.changeset/crazy-seas-eat.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@react-email/components": minor 3 | --- 4 | 5 | Update `@react-email/tailwind@1.1.0-canary.0` 6 | -------------------------------------------------------------------------------- /.changeset/deep-clowns-bet.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@react-email/render": patch 3 | --- 4 | 5 | fix browser version including errors in the output instead of throwing them 6 | -------------------------------------------------------------------------------- /.changeset/great-parrots-yell.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@react-email/tailwind": minor 3 | --- 4 | 5 | Extract tailwind pseudo classes to stylesheet 6 | -------------------------------------------------------------------------------- /.changeset/mighty-pigs-add.md: -------------------------------------------------------------------------------- 1 | --- 2 | "react-email": patch 3 | --- 4 | 5 | Add support for hot reloading with tsconfig path aliases 6 | -------------------------------------------------------------------------------- /.changeset/puny-chicken-argue.md: -------------------------------------------------------------------------------- 1 | --- 2 | "react-email": minor 3 | --- 4 | 5 | use a separate package for storing the preview server (@react-email/preview-server) 6 | -------------------------------------------------------------------------------- /.changeset/stupid-ghosts-decide.md: -------------------------------------------------------------------------------- 1 | --- 2 | "react-email": patch 3 | --- 4 | 5 | Fix prettier errors causing NextJS serialization error 6 | -------------------------------------------------------------------------------- /.changeset/tasty-swans-taste.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@react-email/components": minor 3 | --- 4 | 5 | Updated @react-email/button@0.1.0-canary.0 6 | -------------------------------------------------------------------------------- /.changeset/tidy-geese-cross.md: -------------------------------------------------------------------------------- 1 | --- 2 | "react-email": patch 3 | --- 4 | 5 | Pre-render email templates on hover 6 | -------------------------------------------------------------------------------- /.changeset/tiny-rice-give.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@react-email/button": minor 3 | --- 4 | 5 | respect the order in which padding properties are defined 6 | -------------------------------------------------------------------------------- /.github/DISCUSSION_TEMPLATE/help.yml: -------------------------------------------------------------------------------- 1 | body: 2 | - type: textarea 3 | attributes: 4 | label: Summary 5 | description: What do you need help with? 6 | validations: 7 | required: true 8 | - type: textarea 9 | attributes: 10 | label: Additional information 11 | description: Any code snippets, error messages, or dependency details that may be related? 12 | render: js 13 | validations: 14 | required: false 15 | - type: input 16 | attributes: 17 | label: Example 18 | description: A link to a minimal reproduction is helpful for collaborative debugging! 19 | validations: 20 | required: false 21 | -------------------------------------------------------------------------------- /.github/DISCUSSION_TEMPLATE/ideas.yml: -------------------------------------------------------------------------------- 1 | body: 2 | - type: textarea 3 | attributes: 4 | label: Goals 5 | description: Short list of what the feature request aims to address? 6 | value: | 7 | 1. 8 | 2. 9 | 3. 10 | validations: 11 | required: true 12 | - type: textarea 13 | attributes: 14 | label: Background 15 | description: Discuss prior art, why do you think this feature is needed? Are there current alternatives? 16 | validations: 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: Proposal 21 | description: How should this feature be implemented? Are you interested in contributing? 22 | validations: 23 | required: true 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Ask a question 4 | url: https://github.com/resend/react-email/discussions 5 | about: Ask questions and discuss with other community members 6 | - name: Feature request 7 | url: https://github.com/resend/react-email/discussions/new?category=ideas 8 | about: Feature requests should be opened as discussions 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | **/*/package-lock.json 11 | **/*/yalc.lock 12 | 13 | # next.js 14 | .next/ 15 | out/ 16 | build 17 | dist 18 | .vercel 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | .react-email 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | .pnpm-debug.log* 30 | 31 | # local env files 32 | .env 33 | .env.local 34 | .env.development.local 35 | .env.test.local 36 | .env.production.local 37 | 38 | # turbo 39 | .turbo 40 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | -------------------------------------------------------------------------------- /apps/demo/.gitignore: -------------------------------------------------------------------------------- 1 | .react-email 2 | .vercel 3 | -------------------------------------------------------------------------------- /apps/demo/emails/static/airbnb-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/airbnb-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/airbnb-review-user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/airbnb-review-user.jpg -------------------------------------------------------------------------------- /apps/demo/emails/static/amazon-book.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/amazon-book.jpg -------------------------------------------------------------------------------- /apps/demo/emails/static/amazon-facebook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/amazon-facebook.jpg -------------------------------------------------------------------------------- /apps/demo/emails/static/amazon-instagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/amazon-instagram.jpg -------------------------------------------------------------------------------- /apps/demo/emails/static/amazon-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/amazon-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/amazon-prime-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/amazon-prime-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/amazon-rating.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/amazon-rating.gif -------------------------------------------------------------------------------- /apps/demo/emails/static/amazon-twitter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/amazon-twitter.jpg -------------------------------------------------------------------------------- /apps/demo/emails/static/apple-card-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/apple-card-icon.png -------------------------------------------------------------------------------- /apps/demo/emails/static/apple-hbo-max-icon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/apple-hbo-max-icon.jpeg -------------------------------------------------------------------------------- /apps/demo/emails/static/apple-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/apple-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/apple-wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/apple-wallet.png -------------------------------------------------------------------------------- /apps/demo/emails/static/aws-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/aws-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/codepen-challengers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/codepen-challengers.png -------------------------------------------------------------------------------- /apps/demo/emails/static/codepen-cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/codepen-cube.png -------------------------------------------------------------------------------- /apps/demo/emails/static/codepen-pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/codepen-pro.png -------------------------------------------------------------------------------- /apps/demo/emails/static/dropbox-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/dropbox-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/github.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play-academy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play-academy.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play-chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play-chat.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play-footer.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play-header.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play-icon.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play-pl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play-pl.png -------------------------------------------------------------------------------- /apps/demo/emails/static/google-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/google-play.png -------------------------------------------------------------------------------- /apps/demo/emails/static/koala-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/koala-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/linear-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/linear-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/netlify-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/netlify-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/nike-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/nike-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/nike-phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/nike-phone.png -------------------------------------------------------------------------------- /apps/demo/emails/static/nike-product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/nike-product.png -------------------------------------------------------------------------------- /apps/demo/emails/static/nike-recomendation-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/nike-recomendation-1.png -------------------------------------------------------------------------------- /apps/demo/emails/static/nike-recomendation-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/nike-recomendation-2.png -------------------------------------------------------------------------------- /apps/demo/emails/static/nike-recomendation-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/nike-recomendation-3.png -------------------------------------------------------------------------------- /apps/demo/emails/static/nike-recomendation-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/nike-recomendation-4.png -------------------------------------------------------------------------------- /apps/demo/emails/static/notion-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/notion-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/plaid-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/plaid-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/raycast-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/raycast-bg.png -------------------------------------------------------------------------------- /apps/demo/emails/static/raycast-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/raycast-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/slack-facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/slack-facebook.png -------------------------------------------------------------------------------- /apps/demo/emails/static/slack-linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/slack-linkedin.png -------------------------------------------------------------------------------- /apps/demo/emails/static/slack-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/slack-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/slack-twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/slack-twitter.png -------------------------------------------------------------------------------- /apps/demo/emails/static/stack-overflow-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/stack-overflow-header.png -------------------------------------------------------------------------------- /apps/demo/emails/static/stack-overflow-logo-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/stack-overflow-logo-sm.png -------------------------------------------------------------------------------- /apps/demo/emails/static/stack-overflow-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/stack-overflow-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/stripe-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/stripe-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/twitch-icon-facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/twitch-icon-facebook.png -------------------------------------------------------------------------------- /apps/demo/emails/static/twitch-icon-twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/twitch-icon-twitter.png -------------------------------------------------------------------------------- /apps/demo/emails/static/twitch-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/twitch-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/vercel-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/vercel-arrow.png -------------------------------------------------------------------------------- /apps/demo/emails/static/vercel-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/vercel-logo.png -------------------------------------------------------------------------------- /apps/demo/emails/static/vercel-team.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/vercel-team.png -------------------------------------------------------------------------------- /apps/demo/emails/static/vercel-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/vercel-user.png -------------------------------------------------------------------------------- /apps/demo/emails/static/yelp-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/yelp-footer.png -------------------------------------------------------------------------------- /apps/demo/emails/static/yelp-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/yelp-header.png -------------------------------------------------------------------------------- /apps/demo/emails/static/yelp-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/demo/emails/static/yelp-logo.png -------------------------------------------------------------------------------- /apps/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "pnpm install --frozen-lockfile && email build", 7 | "dev": "email dev", 8 | "start": "email start", 9 | "export": "email export" 10 | }, 11 | "dependencies": { 12 | "@react-email/components": "workspace:*", 13 | "react": "^19", 14 | "react-dom": "^19", 15 | "react-email": "workspace:*" 16 | }, 17 | "devDependencies": { 18 | "@react-email/preview-server": "workspace:*", 19 | "next": "^15.3.2", 20 | "@types/react": "^19", 21 | "@types/react-dom": "^19", 22 | "tsx": "4.19.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/docs/favicon.png -------------------------------------------------------------------------------- /apps/docs/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/docs/images/background.png -------------------------------------------------------------------------------- /apps/docs/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/docs/images/bg.png -------------------------------------------------------------------------------- /apps/docs/images/local-dev.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/docs/images/local-dev.jpg -------------------------------------------------------------------------------- /apps/docs/images/preview-server-vercel-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/docs/images/preview-server-vercel-settings.png -------------------------------------------------------------------------------- /apps/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "mintlify dev" 7 | }, 8 | "dependencies": { 9 | "zod": "3.24.3", 10 | "mintlify": "4.0.494" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/docs/snippets/localdev.mdx: -------------------------------------------------------------------------------- 1 | 1. Clone the repository: 2 | 3 | ```sh 4 | git clone https://github.com/resend/react-email.git 5 | ``` 6 | 7 | 2. Install all dependencies: 8 | 9 | ```sh 10 | pnpm install 11 | ``` 12 | 13 | 3. Run local servers and watch for changes: 14 | 15 | ```sh 16 | pnpm dev 17 | ``` 18 | -------------------------------------------------------------------------------- /apps/docs/snippets/next-steps.mdx: -------------------------------------------------------------------------------- 1 | Try adding these other components to your email. 2 | 3 | 4 | 5 | Display an image in your email. 6 | 7 | 8 | A hyperlink to web pages or anything else a URL can address. 9 | 10 | 11 | Display a divider that separates content areas in your email. 12 | 13 | 18 | A preview text that will be displayed in the inbox of the recipient. 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /apps/web/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.css": "tailwindcss" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/components/code-inline-with-different-colors/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { CodeInline, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | Install the{' '} 7 | 17 | @react-email/components 18 | {' '} 19 | package 20 | 21 | ); 22 | 23 | export default () => { 24 | return {component}; 25 | }; 26 | -------------------------------------------------------------------------------- /apps/web/components/code-inline-with-different-colors/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { CodeInline, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | Install the{' '} 7 | 8 | @react-email/components 9 | {' '} 10 | package 11 | 12 | ); 13 | 14 | export default () => { 15 | return {component}; 16 | }; 17 | -------------------------------------------------------------------------------- /apps/web/components/divider-between-rows-and-columns/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Column, Hr, Row } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | <> 6 | 7 | First column 8 | Second column 9 | 10 |
17 | 18 | First column 19 | Second column 20 | 21 | 22 | ); 23 | 24 | export default () => { 25 | return {component}; 26 | }; 27 | -------------------------------------------------------------------------------- /apps/web/components/divider-between-rows-and-columns/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Column, Hr, Row } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | <> 6 | 7 | First column 8 | Second column 9 | 10 |
11 | 12 | First column 13 | Second column 14 | 15 | 16 | ); 17 | 18 | export default () => { 19 | return {component}; 20 | }; 21 | -------------------------------------------------------------------------------- /apps/web/components/link-inline-with-text/index.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | This is React Email 7 | 8 | ); 9 | 10 | export default () => { 11 | return {component}; 12 | }; 13 | -------------------------------------------------------------------------------- /apps/web/components/markdown-with-container-styles/index.tsx: -------------------------------------------------------------------------------- 1 | import { Markdown } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 10 | {`## Hello, this is my email template 11 | 12 | This is meant to be rendered as a paragraph. There is no way around it. 13 | 14 | ### Another heading that I wrote 15 | `} 16 | 17 | ); 18 | 19 | export default () => { 20 | return {component}; 21 | }; 22 | -------------------------------------------------------------------------------- /apps/web/components/markdown-with-custom-styles/index.tsx: -------------------------------------------------------------------------------- 1 | import { Markdown } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 12 | {`## Hello, this is my email template 13 | 14 | This is meant to be rendered as a paragraph. There is no way around it. 15 | 16 | ### Another heading that I wrote 17 | `} 18 | 19 | ); 20 | 21 | export default () => { 22 | return {component}; 23 | }; 24 | -------------------------------------------------------------------------------- /apps/web/components/one-row-three-columns/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Column, Row } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | 7 | 1/3 8 | 9 | 10 | 1/3 11 | 12 | 13 | 1/3 14 | 15 | 16 | ); 17 | 18 | export default () => { 19 | return {component}; 20 | }; 21 | -------------------------------------------------------------------------------- /apps/web/components/rounded-image/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Img } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | Stagg Electric Kettle 11 | ); 12 | 13 | export default () => { 14 | return {component}; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/components/rounded-image/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Img } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | Stagg Electric Kettle 11 | ); 12 | 13 | export default () => { 14 | return {component}; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/components/section-with-rows-and-columns/index.tsx: -------------------------------------------------------------------------------- 1 | import { Column, Row, Section } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 |
6 | 7 | Column 1, Row 1 8 | Column 2, Row 1 9 | 10 | 11 | Column 1, Row 2 12 | Column 2, Row 2 13 | 14 |
15 | ); 16 | 17 | export default () => { 18 | return {component}; 19 | }; 20 | -------------------------------------------------------------------------------- /apps/web/components/simple-code-inline/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { CodeInline, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | Install the{' '} 7 | 17 | @react-email/components 18 | {' '} 19 | package 20 | 21 | ); 22 | 23 | export default () => { 24 | return {component}; 25 | }; 26 | -------------------------------------------------------------------------------- /apps/web/components/simple-code-inline/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { CodeInline, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | Install the{' '} 7 | 8 | @react-email/components 9 | {' '} 10 | package 11 | 12 | ); 13 | 14 | export default () => { 15 | return {component}; 16 | }; 17 | -------------------------------------------------------------------------------- /apps/web/components/simple-container/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | 9 | Hello, I am a container. I keep content centered and maintain it to a 10 | maximum width while still taking up as much space as possible! 11 | 12 | 13 | ); 14 | 15 | export default () => { 16 | return {component}; 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web/components/simple-container/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | 7 | Hello, I am a container. I keep content centered and maintain it to a 8 | maximum width while still taking up as much space as possible! 9 | 10 | 11 | ); 12 | 13 | export default () => { 14 | return {component}; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/components/simple-divider/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Hr, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | <> 6 | Before divider 7 |
15 | After divider 16 | 17 | ); 18 | 19 | export default () => { 20 | return {component}; 21 | }; 22 | -------------------------------------------------------------------------------- /apps/web/components/simple-divider/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Hr, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | <> 6 | Before divider 7 |
8 | After divider 9 | 10 | ); 11 | 12 | export default () => { 13 | return {component}; 14 | }; 15 | -------------------------------------------------------------------------------- /apps/web/components/simple-heading/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Heading } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | Ray Tomlinson 6 | ); 7 | 8 | export default () => { 9 | return {component}; 10 | }; 11 | -------------------------------------------------------------------------------- /apps/web/components/simple-heading/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Heading } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | Ray Tomlinson 6 | ); 7 | 8 | export default () => { 9 | return {component}; 10 | }; 11 | -------------------------------------------------------------------------------- /apps/web/components/simple-image/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Img } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | Ode Grinder 11 | ); 12 | 13 | export default () => { 14 | return {component}; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/components/simple-image/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Img } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | Ode Grinder 11 | ); 12 | 13 | export default () => { 14 | return {component}; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/components/simple-link/index.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = React Email; 5 | 6 | export default () => { 7 | return {component}; 8 | }; 9 | -------------------------------------------------------------------------------- /apps/web/components/simple-markdown/index.tsx: -------------------------------------------------------------------------------- 1 | import { Markdown } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 6 | {`## Hello, this is my email template 7 | 8 | This is meant to be rendered as a paragraph. There is no way around it. 9 | 10 | ### Another heading that I wrote 11 | `} 12 | 13 | ); 14 | 15 | export default () => { 16 | return {component}; 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web/components/simple-section/index.tsx: -------------------------------------------------------------------------------- 1 | import { Section, Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 |
6 | Hello my section! 7 |
8 | ); 9 | 10 | export default () => { 11 | return {component}; 12 | }; 13 | -------------------------------------------------------------------------------- /apps/web/components/simple-text/index.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = A simple paragraph; 5 | 6 | export default () => { 7 | return {component}; 8 | }; 9 | -------------------------------------------------------------------------------- /apps/web/components/single-button/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 20 | ); 21 | 22 | export default () => { 23 | return {component}; 24 | }; 25 | -------------------------------------------------------------------------------- /apps/web/components/single-button/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | 11 | ); 12 | 13 | export default () => { 14 | return {component}; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/components/static/cube-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/cube-icon.png -------------------------------------------------------------------------------- /apps/web/components/static/download-on-the-app-store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/download-on-the-app-store.png -------------------------------------------------------------------------------- /apps/web/components/static/facebook-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/facebook-logo.png -------------------------------------------------------------------------------- /apps/web/components/static/get-it-on-google-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/get-it-on-google-play.png -------------------------------------------------------------------------------- /apps/web/components/static/heart-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/heart-icon.png -------------------------------------------------------------------------------- /apps/web/components/static/in-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/in-icon.png -------------------------------------------------------------------------------- /apps/web/components/static/instagram-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/instagram-logo.png -------------------------------------------------------------------------------- /apps/web/components/static/logo-without-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/logo-without-background.png -------------------------------------------------------------------------------- /apps/web/components/static/megaphone-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/megaphone-icon.png -------------------------------------------------------------------------------- /apps/web/components/static/rocket-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/rocket-icon.png -------------------------------------------------------------------------------- /apps/web/components/static/steve-jobs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/steve-jobs.jpg -------------------------------------------------------------------------------- /apps/web/components/static/steve-wozniak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/steve-wozniak.jpg -------------------------------------------------------------------------------- /apps/web/components/static/x-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/x-icon.png -------------------------------------------------------------------------------- /apps/web/components/static/x-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/components/static/x-logo.png -------------------------------------------------------------------------------- /apps/web/components/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /apps/web/components/text-with-styling/inline-styles.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | <> 6 | 14 | Amazing content 15 | 16 | 17 | This is the actual content that the accented text above refers to. 18 | 19 | 20 | ); 21 | 22 | export default () => { 23 | return {component}; 24 | }; 25 | -------------------------------------------------------------------------------- /apps/web/components/text-with-styling/tailwind.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from '@react-email/components'; 2 | import { Layout } from '../_components/layout'; 3 | 4 | export const component = ( 5 | <> 6 | 7 | Amazing content 8 | 9 | 10 | This is the actual content that the accented text above refers to. 11 | 12 | 13 | ); 14 | 15 | export default () => { 16 | return {component}; 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web/components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react-jsx" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/web/public/brand/logo-without-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/brand/logo-without-background.png -------------------------------------------------------------------------------- /apps/web/public/brand/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/brand/logo.png -------------------------------------------------------------------------------- /apps/web/public/brand/resend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/brand/resend.png -------------------------------------------------------------------------------- /apps/web/public/examples/airbnb-review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/airbnb-review.png -------------------------------------------------------------------------------- /apps/web/public/examples/apple-receipt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/apple-receipt.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/EmersonGarrido.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/EmersonGarrido.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/Rychillie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/Rychillie.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/abhinandanwadwa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/abhinandanwadwa.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/bruno88cabral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/bruno88cabral.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/bukinoshita.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/bukinoshita.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/c0dr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/c0dr.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/camillegachido.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/camillegachido.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/joaom00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/joaom00.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/nettofarah.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/nettofarah.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/relferreira.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/relferreira.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/ribeiroevandro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/ribeiroevandro.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/thecodeinfluencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/thecodeinfluencer.png -------------------------------------------------------------------------------- /apps/web/public/examples/authors/zenorocha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/authors/zenorocha.png -------------------------------------------------------------------------------- /apps/web/public/examples/aws-verify-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/aws-verify-email.png -------------------------------------------------------------------------------- /apps/web/public/examples/dropbox-reset-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/dropbox-reset-password.png -------------------------------------------------------------------------------- /apps/web/public/examples/github-access-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/github-access-token.png -------------------------------------------------------------------------------- /apps/web/public/examples/google-play-policy-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/google-play-policy-update.png -------------------------------------------------------------------------------- /apps/web/public/examples/koala-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/koala-welcome.png -------------------------------------------------------------------------------- /apps/web/public/examples/linear-login-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/linear-login-code.png -------------------------------------------------------------------------------- /apps/web/public/examples/nike-receipt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/nike-receipt.png -------------------------------------------------------------------------------- /apps/web/public/examples/notion-magic-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/notion-magic-link.png -------------------------------------------------------------------------------- /apps/web/public/examples/plaid-verify-identity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/plaid-verify-identity.png -------------------------------------------------------------------------------- /apps/web/public/examples/raycast-magic-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/raycast-magic-link.png -------------------------------------------------------------------------------- /apps/web/public/examples/slack-confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/slack-confirm.png -------------------------------------------------------------------------------- /apps/web/public/examples/stack-overflow-tips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/stack-overflow-tips.png -------------------------------------------------------------------------------- /apps/web/public/examples/stripe-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/stripe-welcome.png -------------------------------------------------------------------------------- /apps/web/public/examples/twitch-reset-password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/twitch-reset-password.png -------------------------------------------------------------------------------- /apps/web/public/examples/vercel-invite-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/vercel-invite-user.png -------------------------------------------------------------------------------- /apps/web/public/examples/yelp-recent-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/examples/yelp-recent-login.png -------------------------------------------------------------------------------- /apps/web/public/fonts/commit-mono/commit-mono-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/fonts/commit-mono/commit-mono-italic.ttf -------------------------------------------------------------------------------- /apps/web/public/fonts/commit-mono/commit-mono-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/fonts/commit-mono/commit-mono-regular.ttf -------------------------------------------------------------------------------- /apps/web/public/fonts/inter/inter.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/fonts/inter/inter.ttf -------------------------------------------------------------------------------- /apps/web/public/fonts/shantell-sans/shantell-sans-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/fonts/shantell-sans/shantell-sans-italic.ttf -------------------------------------------------------------------------------- /apps/web/public/fonts/shantell-sans/shantell-sans-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/fonts/shantell-sans/shantell-sans-regular.ttf -------------------------------------------------------------------------------- /apps/web/public/meta/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/meta/apple-touch-icon.png -------------------------------------------------------------------------------- /apps/web/public/meta/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/meta/cover.png -------------------------------------------------------------------------------- /apps/web/public/meta/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/meta/favicon.ico -------------------------------------------------------------------------------- /apps/web/public/static/atmos-vacuum-canister.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/atmos-vacuum-canister.jpg -------------------------------------------------------------------------------- /apps/web/public/static/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/bg.png -------------------------------------------------------------------------------- /apps/web/public/static/braun-analogue-clock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/braun-analogue-clock.jpg -------------------------------------------------------------------------------- /apps/web/public/static/braun-classic-watch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/braun-classic-watch.jpg -------------------------------------------------------------------------------- /apps/web/public/static/braun-collection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/braun-collection.jpg -------------------------------------------------------------------------------- /apps/web/public/static/braun-vintage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/braun-vintage.jpg -------------------------------------------------------------------------------- /apps/web/public/static/braun-wall-clock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/braun-wall-clock.jpg -------------------------------------------------------------------------------- /apps/web/public/static/braun-wireless-alarm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/braun-wireless-alarm.jpg -------------------------------------------------------------------------------- /apps/web/public/static/bundle-collection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/bundle-collection.jpg -------------------------------------------------------------------------------- /apps/web/public/static/clara-french-press.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/clara-french-press.jpg -------------------------------------------------------------------------------- /apps/web/public/static/clyde-electric-kettle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/clyde-electric-kettle.jpg -------------------------------------------------------------------------------- /apps/web/public/static/coffee-bean-storage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/coffee-bean-storage.jpg -------------------------------------------------------------------------------- /apps/web/public/static/covers/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/button.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/code-block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/code-block.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/code-inline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/code-inline.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/column.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/column.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/components.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/container.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/create-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/create-email.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/font.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/head.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/heading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/heading.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/hr.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/html.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/img.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/link.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/markdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/markdown.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/patterns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/patterns.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/preview.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/react-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/react-email.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/render.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/row.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/row.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/section.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/section.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/tailwind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/tailwind.png -------------------------------------------------------------------------------- /apps/web/public/static/covers/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/covers/text.png -------------------------------------------------------------------------------- /apps/web/public/static/cube-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/cube-icon.png -------------------------------------------------------------------------------- /apps/web/public/static/download-on-the-app-store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/download-on-the-app-store.png -------------------------------------------------------------------------------- /apps/web/public/static/facebook-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/facebook-logo.png -------------------------------------------------------------------------------- /apps/web/public/static/get-it-on-google-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/get-it-on-google-play.png -------------------------------------------------------------------------------- /apps/web/public/static/grinder-collection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/grinder-collection.jpg -------------------------------------------------------------------------------- /apps/web/public/static/heart-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/heart-icon.png -------------------------------------------------------------------------------- /apps/web/public/static/herman-miller-chair.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/herman-miller-chair.jpg -------------------------------------------------------------------------------- /apps/web/public/static/in-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/in-icon.png -------------------------------------------------------------------------------- /apps/web/public/static/instagram-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/instagram-logo.png -------------------------------------------------------------------------------- /apps/web/public/static/logo-without-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/logo-without-background.png -------------------------------------------------------------------------------- /apps/web/public/static/megaphone-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/megaphone-icon.png -------------------------------------------------------------------------------- /apps/web/public/static/monty-art-cup-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/monty-art-cup-1.jpg -------------------------------------------------------------------------------- /apps/web/public/static/monty-art-cup-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/monty-art-cup-2.jpg -------------------------------------------------------------------------------- /apps/web/public/static/mugs-collection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/mugs-collection.jpg -------------------------------------------------------------------------------- /apps/web/public/static/ode-grinder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/ode-grinder.jpg -------------------------------------------------------------------------------- /apps/web/public/static/outdoor-living.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/outdoor-living.jpg -------------------------------------------------------------------------------- /apps/web/public/static/rocket-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/rocket-icon.png -------------------------------------------------------------------------------- /apps/web/public/static/stagg-eletric-kettle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/stagg-eletric-kettle.jpg -------------------------------------------------------------------------------- /apps/web/public/static/steve-jobs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/steve-jobs.jpg -------------------------------------------------------------------------------- /apps/web/public/static/steve-wozniak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/steve-wozniak.jpg -------------------------------------------------------------------------------- /apps/web/public/static/vacuum-canister-clear-glass-bundle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/vacuum-canister-clear-glass-bundle.jpg -------------------------------------------------------------------------------- /apps/web/public/static/versatile-comfort.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/versatile-comfort.jpg -------------------------------------------------------------------------------- /apps/web/public/static/x-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/x-icon.png -------------------------------------------------------------------------------- /apps/web/public/static/x-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/apps/web/public/static/x-logo.png -------------------------------------------------------------------------------- /apps/web/src/app/robots.ts: -------------------------------------------------------------------------------- 1 | const Robots = () => { 2 | return { 3 | rules: [ 4 | { 5 | userAgent: '*', 6 | allow: '/', 7 | }, 8 | ], 9 | sitemap: 'https://react.email/sitemap.xml', 10 | host: 'https://react.email', 11 | }; 12 | }; 13 | 14 | export default Robots; 15 | -------------------------------------------------------------------------------- /apps/web/src/app/sitemap.ts: -------------------------------------------------------------------------------- 1 | const Sitemap = async () => { 2 | const routes = ['', '/components', '/examples'].map((route) => ({ 3 | url: `https://react.email${route}`, 4 | lastModified: new Date().toISOString().split('T')[0], 5 | })); 6 | 7 | return [...routes]; 8 | }; 9 | 10 | export default Sitemap; 11 | -------------------------------------------------------------------------------- /apps/web/src/components/anchor.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import type * as React from 'react'; 3 | 4 | export const Anchor: React.FC< 5 | Readonly> 6 | > = ({ className, ...props }) => ( 7 | 17 | {props.children} 18 | 19 | ); 20 | -------------------------------------------------------------------------------- /apps/web/src/components/components-view.tsx: -------------------------------------------------------------------------------- 1 | import type { ImportedComponent } from '../app/components/get-imported-components-for'; 2 | import { ComponentView } from './component-view'; 3 | 4 | interface ComponentsViewProps { 5 | components: ImportedComponent[]; 6 | } 7 | 8 | export const ComponentsView: React.FC = ({ 9 | components, 10 | }) => { 11 | return ( 12 | <> 13 | {components.map((component, index) => ( 14 | 19 | ))} 20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /apps/web/src/components/icon-button.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import type * as React from 'react'; 3 | 4 | type IconButtonProps = React.ComponentPropsWithoutRef<'button'>; 5 | 6 | export const IconButton: React.FC> = ({ 7 | children, 8 | className, 9 | ...props 10 | }) => ( 11 | 21 | ); 22 | -------------------------------------------------------------------------------- /apps/web/src/components/icons/icon-base.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type IconElement = SVGSVGElement; 4 | export interface IconProps extends React.ComponentPropsWithoutRef<'svg'> { 5 | size?: number; 6 | } 7 | 8 | export const IconBase = React.forwardRef>( 9 | ({ size = 20, ...props }, forwardedRef) => ( 10 | 19 | ), 20 | ); 21 | 22 | IconBase.displayName = 'IconBase'; 23 | -------------------------------------------------------------------------------- /apps/web/src/components/icons/icon-source.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import type { IconElement, IconProps } from './icon-base'; 3 | import { IconBase } from './icon-base'; 4 | 5 | export const IconSource = React.forwardRef>( 6 | ({ ...props }, forwardedRef) => ( 7 | 8 | 15 | 16 | ), 17 | ); 18 | 19 | IconSource.displayName = 'IconSource'; 20 | -------------------------------------------------------------------------------- /apps/web/src/components/tooltip.tsx: -------------------------------------------------------------------------------- 1 | import * as TooltipPrimitive from '@radix-ui/react-tooltip'; 2 | import type * as React from 'react'; 3 | import { TooltipContent } from './tooltip-content'; 4 | 5 | type RootProps = React.ComponentPropsWithoutRef; 6 | 7 | export type TooltipProps = RootProps; 8 | 9 | export const TooltipRoot: React.FC> = ({ 10 | children, 11 | ...props 12 | }) => {children}; 13 | 14 | export const Tooltip = Object.assign(TooltipRoot, { 15 | Arrow: TooltipPrimitive.TooltipArrow, 16 | Provider: TooltipPrimitive.TooltipProvider, 17 | Content: TooltipContent, 18 | Trigger: TooltipPrimitive.TooltipTrigger, 19 | }); 20 | -------------------------------------------------------------------------------- /apps/web/src/components/topbar.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import Link from 'next/link'; 3 | import type * as React from 'react'; 4 | import { Logo } from './logo'; 5 | import { Menu } from './menu'; 6 | 7 | export const Topbar: React.FC< 8 | Readonly> 9 | > = ({ className, ...props }) => ( 10 |
17 | 21 | 22 | 23 | 24 |
25 | ); 26 | -------------------------------------------------------------------------------- /apps/web/src/hooks/use-stored-state.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const useStoredState = ( 4 | key: string, 5 | defaultValue: T, 6 | ): [state: T, setState: (newValue: T) => void] => { 7 | const [state, setState] = React.useState(defaultValue); 8 | React.useEffect(() => { 9 | const storedValue = localStorage.getItem(key); 10 | if (storedValue) { 11 | setState(storedValue as T); 12 | } 13 | }, [key]); 14 | 15 | return [ 16 | state, 17 | (newValue) => { 18 | if (newValue) { 19 | localStorage.setItem(key, newValue); 20 | } 21 | setState(newValue); 22 | }, 23 | ]; 24 | }; 25 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/articles.tsx: -------------------------------------------------------------------------------- 1 | const IllustrationArticles: React.FC = () => ( 2 |
3 |
4 |
5 |
6 |
7 |
8 | ); 9 | 10 | export default IllustrationArticles; 11 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/buttons.tsx: -------------------------------------------------------------------------------- 1 | import { MousePointer2Icon } from 'lucide-react'; 2 | 3 | const IllustrationButtons: React.FC = () => ( 4 |
5 |
6 | 11 |
12 | ); 13 | 14 | export default IllustrationButtons; 15 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/code-inline.tsx: -------------------------------------------------------------------------------- 1 | import { ChevronRightIcon } from 'lucide-react'; 2 | 3 | const IllustrationCodeInline: React.FC = () => ( 4 |
5 | 6 |
7 |
8 |
9 | ); 10 | 11 | export default IllustrationCodeInline; 12 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/divider.tsx: -------------------------------------------------------------------------------- 1 | const IllustrationDivider: React.FC = () => ( 2 |
3 |
4 |
5 |
6 |
7 | ); 8 | 9 | export default IllustrationDivider; 10 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/heading.tsx: -------------------------------------------------------------------------------- 1 | const IllustrationHeading: React.FC = () => ( 2 |
3 |
4 |
5 |
6 |
7 |
8 | ); 9 | 10 | export default IllustrationHeading; 11 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/image.tsx: -------------------------------------------------------------------------------- 1 | import { ImageIcon } from 'lucide-react'; 2 | 3 | const IllustrationImage: React.FC = () => ( 4 |
5 | 6 |
7 | ); 8 | 9 | export default IllustrationImage; 10 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/section.tsx: -------------------------------------------------------------------------------- 1 | const IllustrationSection: React.FC = () => ( 2 |
3 | ); 4 | 5 | export default IllustrationSection; 6 | -------------------------------------------------------------------------------- /apps/web/src/illustrations/text.tsx: -------------------------------------------------------------------------------- 1 | const IllustrationText: React.FC = () => ( 2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ); 10 | 11 | export default IllustrationText; 12 | -------------------------------------------------------------------------------- /apps/web/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @media (prefers-reduced-motion: no-preference) { 6 | * { 7 | scroll-behavior: smooth; 8 | } 9 | } 10 | 11 | #root, 12 | #__next { 13 | isolation: isolate; 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/src/utils/as.ts: -------------------------------------------------------------------------------- 1 | export type As< 2 | DefaultTag extends React.ElementType, 3 | T1 extends React.ElementType, 4 | T2 extends React.ElementType = T1, 5 | T3 extends React.ElementType = T1, 6 | T4 extends React.ElementType = T1, 7 | T5 extends React.ElementType = T1, 8 | > = 9 | | (React.ComponentPropsWithRef & { 10 | as?: DefaultTag; 11 | }) 12 | | (React.ComponentPropsWithRef & { 13 | as: T1; 14 | }) 15 | | (React.ComponentPropsWithRef & { 16 | as: T2; 17 | }) 18 | | (React.ComponentPropsWithRef & { 19 | as: T3; 20 | }) 21 | | (React.ComponentPropsWithRef & { 22 | as: T4; 23 | }) 24 | | (React.ComponentPropsWithRef & { 25 | as: T5; 26 | }); 27 | -------------------------------------------------------------------------------- /apps/web/src/utils/slugify.ts: -------------------------------------------------------------------------------- 1 | export const slugify = (text: string): string => 2 | text 3 | .toLowerCase() 4 | .trim() 5 | .replace(/\s+/g, '-') 6 | .replace(/[^\w-]+/g, '') 7 | .replace(/--+/g, '-'); 8 | -------------------------------------------------------------------------------- /apps/web/src/utils/unreachable.ts: -------------------------------------------------------------------------------- 1 | export const unreachable = ( 2 | condition: never, 3 | message = `Entered unreachable code. Received '${ 4 | typeof condition === 'string' ? condition : JSON.stringify(condition) 5 | }'.`, 6 | ): never => { 7 | throw new TypeError(message); 8 | }; 9 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/nextjs.json", 3 | "compilerOptions": { 4 | "target": "ES2018", 5 | "plugins": [{ "name": "next" }], 6 | "paths": { "@/*": ["./src/*"] }, 7 | "types": ["vitest/globals"] 8 | }, 9 | "include": [ 10 | "next-env.d.ts", 11 | "**/*.ts", 12 | "**/*.tsx", 13 | ".next/types/**/*.ts", 14 | "next.config.js" 15 | ], 16 | "exclude": ["node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /apps/web/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { loadEnvConfig } from '@next/env'; 3 | import { defineConfig } from 'vitest/config'; 4 | 5 | loadEnvConfig(__dirname, true); 6 | 7 | export default defineConfig({ 8 | test: { 9 | globals: true, 10 | environment: 'jsdom', 11 | }, 12 | esbuild: { 13 | tsconfigRaw: { 14 | compilerOptions: { 15 | jsx: 'react-jsx', 16 | }, 17 | }, 18 | }, 19 | resolve: { 20 | alias: { 21 | '@': path.resolve(__dirname, './src'), 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /benchmarks/tailwind-component/.gitignore: -------------------------------------------------------------------------------- 1 | isolate-*.log 2 | flamegraph.html 3 | .preview 4 | .vscpreview 5 | -------------------------------------------------------------------------------- /benchmarks/tailwind-component/src/tailwind-render.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/render'; 2 | import { Tailwind as CurrentTailwind } from '@react-email/tailwind'; 3 | import EmailWithTailwind from './emails/with-tailwind.js'; 4 | 5 | await render(); 6 | -------------------------------------------------------------------------------- /benchmarks/tailwind-component/tailwind.config.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/benchmarks/tailwind-component/tailwind.config.js -------------------------------------------------------------------------------- /benchmarks/tailwind-component/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "tsconfig/react-library.json", 4 | "include": ["src"], 5 | "exclude": ["dist", "build", "node_modules"], 6 | "compilerOptions": { 7 | "target": "esnext", 8 | "noUncheckedIndexedAccess": true, 9 | "resolveJsonModule": true, 10 | "moduleResolution": "Node", 11 | "declarationMap": false, 12 | "declaration": false, 13 | "outDir": "dist" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/aws-ses/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-aws-ses", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "main": "./dist/index.js", 8 | "files": [ 9 | "dist/**" 10 | ], 11 | "scripts": { 12 | "build": "tsup-node src/index.tsx --format esm --target node20", 13 | "dev": "tsup-node src/index.tsx --format esm --target node20 --watch", 14 | "clean": "rm -rf dist" 15 | }, 16 | "dependencies": { 17 | "@aws-sdk/client-ses": "^3", 18 | "@react-email/components": "^0.0.36", 19 | "react": "^19", 20 | "react-dom": "^19" 21 | }, 22 | "devDependencies": { 23 | "tsup": "^8.0.0", 24 | "typescript": "^4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/aws-ses/src/email.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Html } from '@react-email/components'; 2 | import type * as React from 'react'; 3 | 4 | interface EmailProps { 5 | url: string; 6 | } 7 | 8 | export const Email: React.FC> = ({ url }) => { 9 | return ( 10 | 11 | 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /examples/mailersend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-mailersend", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "main": "./dist/index.js", 8 | "files": [ 9 | "dist/**" 10 | ], 11 | "scripts": { 12 | "build": "tsup-node src/index.tsx --format esm --target node20", 13 | "dev": "tsup-node src/index.tsx --format esm --target node20 --watch", 14 | "clean": "rm -rf dist" 15 | }, 16 | "dependencies": { 17 | "@react-email/components": "^0.0.36", 18 | "mailersend": "^2", 19 | "react": "^19", 20 | "react-dom": "^19" 21 | }, 22 | "devDependencies": { 23 | "tsup": "^8.0.0", 24 | "typescript": "^4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/mailersend/src/email.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Html } from '@react-email/components'; 2 | 3 | interface EmailProps { 4 | url: string; 5 | } 6 | 7 | export const Email: React.FC> = ({ url }) => { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /examples/mailersend/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/components'; 2 | import { EmailParams, MailerSend, Recipient, Sender } from 'mailersend'; 3 | import { Email } from './email'; 4 | 5 | const mailerSend = new MailerSend({ 6 | apiKey: process.env.MAILERSEND_API_KEY || '', 7 | }); 8 | 9 | const emailHtml = await render(); 10 | 11 | const sentFrom = new Sender('you@yourdomain.com', 'Your name'); 12 | const recipients = [new Recipient('your@client.com', 'Your Client')]; 13 | 14 | const emailParams = new EmailParams() 15 | .setFrom(sentFrom) 16 | .setTo(recipients) 17 | .setSubject('This is a Subject') 18 | .setHtml(emailHtml); 19 | 20 | await mailerSend.email.send(emailParams); 21 | -------------------------------------------------------------------------------- /examples/nodemailer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-nodemailer", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "main": "./dist/index.js", 8 | "files": [ 9 | "dist/**" 10 | ], 11 | "scripts": { 12 | "build": "tsup-node src/index.tsx --format esm --target node20", 13 | "dev": "tsup-node src/index.tsx --format esm --target node20 --watch", 14 | "clean": "rm -rf dist" 15 | }, 16 | "dependencies": { 17 | "@react-email/components": "^0.0.36", 18 | "nodemailer": "^6", 19 | "react": "^19", 20 | "react-dom": "^19" 21 | }, 22 | "devDependencies": { 23 | "@types/nodemailer": "^6", 24 | "tsup": "^8.0.0", 25 | "typescript": "^4" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/nodemailer/src/email.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Html } from '@react-email/components'; 2 | 3 | interface EmailProps { 4 | url: string; 5 | } 6 | 7 | export const Email: React.FC> = ({ url }) => { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /examples/nodemailer/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/components'; 2 | import nodemailer from 'nodemailer'; 3 | import { Email } from './email'; 4 | 5 | const transporter = nodemailer.createTransport({ 6 | host: 'smtp.forwardemail.net', 7 | port: 465, 8 | secure: true, 9 | auth: { 10 | user: 'my_user', 11 | pass: 'my_password', 12 | }, 13 | }); 14 | 15 | const emailHtml = await render(); 16 | 17 | const options = { 18 | from: 'you@example.com', 19 | to: 'user@gmail.com', 20 | subject: 'hello world', 21 | html: emailHtml, 22 | }; 23 | 24 | await transporter.sendMail(options); 25 | -------------------------------------------------------------------------------- /examples/plunk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-plunk", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "main": "./dist/index.js", 8 | "files": [ 9 | "dist/**" 10 | ], 11 | "scripts": { 12 | "build": "tsup-node src/index.tsx --format esm --target node20", 13 | "dev": "tsup-node src/index.tsx --format esm --target node20 --watch", 14 | "clean": "rm -rf dist" 15 | }, 16 | "dependencies": { 17 | "@plunk/node": "^3", 18 | "@react-email/components": "^0.0.36", 19 | "react": "^19", 20 | "react-dom": "^19" 21 | }, 22 | "devDependencies": { 23 | "tsup": "^8.0.0", 24 | "typescript": "^4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/plunk/src/email.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Html } from '@react-email/components'; 2 | 3 | interface EmailProps { 4 | url: string; 5 | } 6 | 7 | export const Email: React.FC> = ({ url }) => { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /examples/plunk/src/index.tsx: -------------------------------------------------------------------------------- 1 | import plunkImport from '@plunk/node'; 2 | import { render } from '@react-email/components'; 3 | import { Email } from './email'; 4 | 5 | const Plunk = ( 6 | plunkImport as unknown as { 7 | default: typeof plunkImport; 8 | } 9 | ).default; 10 | 11 | // See https://github.com/useplunk/node/issues/2 for why Plunk.default 12 | const plunk = new Plunk(process.env.PLUNK_API_KEY || ''); 13 | 14 | const emailHtml = await render(); 15 | 16 | await plunk.emails.send({ 17 | to: 'hello@useplunk.com', 18 | subject: 'Hello world', 19 | body: emailHtml, 20 | }); 21 | -------------------------------------------------------------------------------- /examples/postmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-postmark", 3 | "license": "MIT", 4 | "private": true, 5 | "type": "module", 6 | "sideEffects": false, 7 | "main": "./dist/index.js", 8 | "files": [ 9 | "dist/**" 10 | ], 11 | "scripts": { 12 | "build": "tsup-node src/index.tsx --format esm --target node20", 13 | "dev": "tsup-node src/index.tsx --format esm --target node20 --watch", 14 | "clean": "rm -rf dist" 15 | }, 16 | "dependencies": { 17 | "postmark": "^3", 18 | "@react-email/components": "^0.0.36", 19 | "react": "^19", 20 | "react-dom": "^19" 21 | }, 22 | "devDependencies": { 23 | "tsup": "^8.0.0", 24 | "typescript": "^4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/postmark/src/email.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Html } from '@react-email/components'; 2 | 3 | interface EmailProps { 4 | url: string; 5 | } 6 | 7 | export const Email: React.FC> = ({ url }) => { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /examples/postmark/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/components'; 2 | import postmark from 'postmark'; 3 | import { Email } from './email'; 4 | 5 | const client = new postmark.ServerClient(process.env.POSTMARK_API_KEY || ''); 6 | 7 | const emailHtml = await render(); 8 | 9 | const options = { 10 | From: 'you@example.com', 11 | To: 'user@gmail.com', 12 | Subject: 'hello world', 13 | HtmlBody: emailHtml, 14 | }; 15 | 16 | await client.sendEmail(options); 17 | -------------------------------------------------------------------------------- /examples/resend/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/resend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-resend", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "scripts": { 7 | "build": "next build", 8 | "dev": "next dev", 9 | "start": "next start" 10 | }, 11 | "dependencies": { 12 | "next": "^15", 13 | "@react-email/components": "^0.0.36", 14 | "react": "^19", 15 | "react-dom": "^19", 16 | "resend": "^4" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^22.0.0", 20 | "@types/react": "^19", 21 | "@types/react-dom": "^19", 22 | "typescript": "^5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/resend/src/lib/resend.ts: -------------------------------------------------------------------------------- 1 | import { Resend } from 'resend'; 2 | 3 | export const resend = new Resend(process.env.RESEND_API_KEY); 4 | -------------------------------------------------------------------------------- /examples/resend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": false, 7 | "forceConsistentCasingInFileNames": true, 8 | "strictNullChecks": true, 9 | "noEmit": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "target": "ES2017" 18 | }, 19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /examples/scaleway/next/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/scaleway/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-next-scaleway", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "scripts": { 7 | "build": "next build", 8 | "dev": "next dev", 9 | "start": "next start" 10 | }, 11 | "dependencies": { 12 | "@scaleway/sdk": "^1", 13 | "next": "^15", 14 | "@react-email/components": "^0.0.36", 15 | "react": "^19", 16 | "react-dom": "^19" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^22.0.0", 20 | "@types/react": "^19", 21 | "@types/react-dom": "^19", 22 | "typescript": "^4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/scaleway/next/src/lib/scaleway.ts: -------------------------------------------------------------------------------- 1 | import { createClient, TransactionalEmail } from '@scaleway/sdk'; 2 | 3 | const client = createClient({ 4 | accessKey: process.env.ACCESS_KEY, 5 | secretKey: process.env.SECRET_KEY, 6 | defaultProjectId: process.env.PROJECT_ID, 7 | defaultRegion: 'fr-par', 8 | defaultZone: 'fr-par-1', 9 | }); 10 | 11 | export const scalewayTEM = new TransactionalEmail.v1alpha1.API(client); 12 | -------------------------------------------------------------------------------- /examples/scaleway/next/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": false, 7 | "forceConsistentCasingInFileNames": true, 8 | "noEmit": true, 9 | "incremental": true, 10 | "strictNullChecks": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "target": "ES2017" 18 | }, 19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /examples/scaleway/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-node-scaleway", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "main": "./dist/index.js", 7 | "type": "module", 8 | "files": [ 9 | "dist/**" 10 | ], 11 | "scripts": { 12 | "build": "tsup-node src/index.tsx --format esm --target node20", 13 | "dev": "tsup-node src/index.tsx --format esm --target node20 --watch" 14 | }, 15 | "dependencies": { 16 | "@scaleway/sdk": "^1", 17 | "@react-email/components": "^0.0.36", 18 | "react": "^19", 19 | "react-dom": "^19" 20 | }, 21 | "devDependencies": { 22 | "tsup": "^8.0.0", 23 | "typescript": "^4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/sendgrid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-with-sendgrid", 3 | "license": "MIT", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "main": "./dist/index.js", 8 | "files": [ 9 | "dist/**" 10 | ], 11 | "scripts": { 12 | "build": "tsup-node src/index.tsx --format esm --target node20", 13 | "dev": "tsup-node src/index.tsx --format esm --target node20 --watch", 14 | "clean": "rm -rf dist" 15 | }, 16 | "dependencies": { 17 | "@sendgrid/mail": "^7", 18 | "@react-email/components": "^0.0.36", 19 | "react": "^19", 20 | "react-dom": "^19" 21 | }, 22 | "devDependencies": { 23 | "tsup": "^8.0.0", 24 | "typescript": "^4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/sendgrid/src/email.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Html } from '@react-email/components'; 2 | 3 | interface EmailProps { 4 | url: string; 5 | } 6 | 7 | export const Email: React.FC> = ({ url }) => { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /examples/sendgrid/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/components'; 2 | import sendgrid from '@sendgrid/mail'; 3 | import { Email } from './email'; 4 | 5 | sendgrid.setApiKey(process.env.SENDGRID_API_KEY || ''); 6 | 7 | const emailHtml = await render(); 8 | 9 | const options = { 10 | from: 'you@example.com', 11 | to: 'user@gmail.com', 12 | subject: 'hello world', 13 | html: emailHtml, 14 | }; 15 | 16 | await sendgrid.send(options); 17 | -------------------------------------------------------------------------------- /packages/body/src/__snapshots__/body.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = `"Lorem ipsum"`; 4 | -------------------------------------------------------------------------------- /packages/body/src/body.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type BodyProps = Readonly>; 4 | 5 | export const Body = React.forwardRef( 6 | ({ children, style, ...props }, ref) => { 7 | return ( 8 | 9 | {children} 10 | 11 | ); 12 | }, 13 | ); 14 | 15 | Body.displayName = 'Body'; 16 | -------------------------------------------------------------------------------- /packages/body/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './body'; 2 | -------------------------------------------------------------------------------- /packages/body/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/button/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | -------------------------------------------------------------------------------- /packages/button/src/utils/px-to-pt.ts: -------------------------------------------------------------------------------- 1 | export const pxToPt = (px: number | undefined): number | undefined => 2 | typeof px === 'number' && !Number.isNaN(Number(px)) 3 | ? (px * 3) / 4 4 | : undefined; 5 | -------------------------------------------------------------------------------- /packages/button/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/code-block/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './code-block'; 2 | export * from './languages-available'; 3 | export * from './themes'; 4 | -------------------------------------------------------------------------------- /packages/code-block/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/code-inline/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './code-inline'; 2 | -------------------------------------------------------------------------------- /packages/code-inline/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/column/src/__snapshots__/column.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = `"Lorem ipsum"`; 4 | -------------------------------------------------------------------------------- /packages/column/src/column.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type ColumnProps = Readonly>; 4 | 5 | export const Column = React.forwardRef( 6 | ({ children, style, ...props }, ref) => { 7 | return ( 8 | 9 | {children} 10 | 11 | ); 12 | }, 13 | ); 14 | 15 | Column.displayName = 'Column'; 16 | -------------------------------------------------------------------------------- /packages/column/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './column'; 2 | -------------------------------------------------------------------------------- /packages/column/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/components/src/__snapshots__/heading.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`render renders the component 1`] = `"

Lorem ipsum

"`; 4 | -------------------------------------------------------------------------------- /packages/components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/container/src/__snapshots__/container.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = `"
"`; 4 | -------------------------------------------------------------------------------- /packages/container/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './container'; 2 | -------------------------------------------------------------------------------- /packages/container/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/create-email/.gitignore: -------------------------------------------------------------------------------- 1 | .test 2 | -------------------------------------------------------------------------------- /packages/create-email/.npmignore: -------------------------------------------------------------------------------- 1 | .test 2 | template/node_modules 3 | template/CHANGELOG.md 4 | template/.turbo 5 | template/.react-email 6 | -------------------------------------------------------------------------------- /packages/create-email/template/.gitignore: -------------------------------------------------------------------------------- 1 | .react-email -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/notion-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/notion-logo.png -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/plaid-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/plaid-logo.png -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/plaid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/plaid.png -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/stripe-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/stripe-logo.png -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/vercel-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/vercel-arrow.png -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/vercel-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/vercel-logo.png -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/vercel-team.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/vercel-team.png -------------------------------------------------------------------------------- /packages/create-email/template/emails/static/vercel-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/create-email/template/emails/static/vercel-user.png -------------------------------------------------------------------------------- /packages/create-email/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-email-starter", 3 | "version": "1.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "email build", 7 | "dev": "email dev", 8 | "export": "email export" 9 | }, 10 | "dependencies": { 11 | "@react-email/components": "INSERT_COMPONENTS_VERSION", 12 | "react-dom": "^19.0.0", 13 | "react": "^19.0.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^19.0.1", 17 | "@types/react-dom": "^19.0.1", 18 | "react-email": "INSERT_REACT_EMAIL_VERSION" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/create-email/template/readme.md: -------------------------------------------------------------------------------- 1 | # React Email Starter 2 | 3 | A live preview right in your browser so you don't need to keep sending real emails during development. 4 | 5 | ## Getting Started 6 | 7 | First, install the dependencies: 8 | 9 | ```sh 10 | npm install 11 | # or 12 | yarn 13 | ``` 14 | 15 | Then, run the development server: 16 | 17 | ```sh 18 | npm run dev 19 | # or 20 | yarn dev 21 | ``` 22 | 23 | Open [localhost:3000](http://localhost:3000) with your browser to see the result. 24 | 25 | ## License 26 | 27 | MIT License 28 | -------------------------------------------------------------------------------- /packages/create-email/template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": false, 4 | "jsx": "react-jsx", 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "strictNullChecks": true 9 | }, 10 | "include": ["**/*.ts", "**/*.tsx"], 11 | "exclude": ["node_modules", ".react-email"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/create-email/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/base.json", 3 | "include": ["**/*.ts", "**/*.tsx"], 4 | "exclude": ["dist", "build", "node_modules", ".test"], 5 | "compilerOptions": { 6 | "noEmit": true, 7 | "types": ["vitest/globals"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/create-email/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | environment: 'happy-dom', 7 | exclude: ['.test/**/*', 'template/**/*', '**/node_modules'], 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /packages/font/src/__snapshots__/font.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = ` 4 | "" 17 | `; 18 | -------------------------------------------------------------------------------- /packages/font/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './font'; 2 | -------------------------------------------------------------------------------- /packages/font/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/head/src/head.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type HeadProps = Readonly>; 4 | 5 | export const Head = React.forwardRef( 6 | ({ children, ...props }, ref) => ( 7 | 8 | 9 | 10 | {children} 11 | 12 | ), 13 | ); 14 | 15 | Head.displayName = 'Head'; 16 | -------------------------------------------------------------------------------- /packages/head/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './head'; 2 | -------------------------------------------------------------------------------- /packages/head/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/heading/src/__snapshots__/heading.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`render > renders the component 1`] = `"

Lorem ipsum

"`; 4 | -------------------------------------------------------------------------------- /packages/heading/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './heading'; 2 | -------------------------------------------------------------------------------- /packages/heading/src/utils/as.ts: -------------------------------------------------------------------------------- 1 | export type As< 2 | DefaultTag extends React.ElementType, 3 | T1 extends React.ElementType, 4 | T2 extends React.ElementType = T1, 5 | T3 extends React.ElementType = T1, 6 | T4 extends React.ElementType = T1, 7 | T5 extends React.ElementType = T1, 8 | > = 9 | | (React.ComponentPropsWithRef & { 10 | as?: DefaultTag; 11 | }) 12 | | (React.ComponentPropsWithRef & { 13 | as: T1; 14 | }) 15 | | (React.ComponentPropsWithRef & { 16 | as: T2; 17 | }) 18 | | (React.ComponentPropsWithRef & { 19 | as: T3; 20 | }) 21 | | (React.ComponentPropsWithRef & { 22 | as: T4; 23 | }) 24 | | (React.ComponentPropsWithRef & { 25 | as: T5; 26 | }); 27 | -------------------------------------------------------------------------------- /packages/heading/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/hr/src/__snapshots__/hr.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`
component > renders correctly 1`] = `"
"`; 4 | -------------------------------------------------------------------------------- /packages/hr/src/hr.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/render'; 2 | import { Hr } from './index'; 3 | 4 | describe('
component', () => { 5 | it('passes styles and other props correctly', async () => { 6 | const style = { 7 | width: '50%', 8 | borderColor: 'black', 9 | }; 10 | const html = await render(
); 11 | expect(html).toContain('width:50%'); 12 | expect(html).toContain('border-color:black'); 13 | expect(html).toContain('data-testid="hr-test"'); 14 | }); 15 | 16 | it('renders correctly', async () => { 17 | const actualOutput = await render(
); 18 | expect(actualOutput).toMatchSnapshot(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/hr/src/hr.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type HrProps = Readonly>; 4 | 5 | export const Hr = React.forwardRef( 6 | ({ style, ...props }, ref) => ( 7 |
17 | ), 18 | ); 19 | 20 | Hr.displayName = 'Hr'; 21 | -------------------------------------------------------------------------------- /packages/hr/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hr'; 2 | -------------------------------------------------------------------------------- /packages/hr/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/html/src/__snapshots__/html.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = `""`; 4 | -------------------------------------------------------------------------------- /packages/html/src/html.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type HtmlProps = Readonly>; 4 | 5 | export const Html = React.forwardRef( 6 | ({ children, lang = 'en', dir = 'ltr', ...props }, ref) => ( 7 | 8 | {children} 9 | 10 | ), 11 | ); 12 | 13 | Html.displayName = 'Html'; 14 | -------------------------------------------------------------------------------- /packages/html/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './html'; 2 | -------------------------------------------------------------------------------- /packages/html/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/img/src/__snapshots__/img.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = `"Cat"`; 4 | -------------------------------------------------------------------------------- /packages/img/src/img.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type ImgProps = Readonly>; 4 | 5 | export const Img = React.forwardRef( 6 | ({ alt, src, width, height, style, ...props }, ref) => ( 7 | {alt} 22 | ), 23 | ); 24 | 25 | Img.displayName = 'Img'; 26 | -------------------------------------------------------------------------------- /packages/img/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './img'; 2 | -------------------------------------------------------------------------------- /packages/img/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/link/src/__snapshots__/link.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = `"Example"`; 4 | -------------------------------------------------------------------------------- /packages/link/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './link'; 2 | -------------------------------------------------------------------------------- /packages/link/src/link.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type LinkProps = Readonly>; 4 | 5 | export const Link = React.forwardRef( 6 | ({ target = '_blank', style, ...props }, ref) => ( 7 | 17 | {props.children} 18 | 19 | ), 20 | ); 21 | 22 | Link.displayName = 'Link'; 23 | -------------------------------------------------------------------------------- /packages/link/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/markdown/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './markdown'; 2 | -------------------------------------------------------------------------------- /packages/markdown/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/preview-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | 4 | # for testing 5 | static 6 | -------------------------------------------------------------------------------- /packages/preview-server/.npmignore: -------------------------------------------------------------------------------- 1 | .react-email 2 | ./emails 3 | ./emails/static 4 | node_modules 5 | .turbo 6 | -------------------------------------------------------------------------------- /packages/preview-server/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @react-email/preview-server 2 | 3 | ## 1.0.0-canary.1 4 | 5 | ### Patch Changes 6 | 7 | - efb4db2: fix `` not being flagged as incompatible 8 | -------------------------------------------------------------------------------- /packages/preview-server/_index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this file is just a placeholder file so that import.meta.resolve and require.resolve can properly 3 | * find out the path to this module. This file does not do anything nor does it need to export anything of value. 4 | */ 5 | -------------------------------------------------------------------------------- /packages/preview-server/module-punycode.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'module-punycode' { 2 | export * from 'node:punycode'; 3 | } 4 | -------------------------------------------------------------------------------- /packages/preview-server/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/preview-server/postcss.config.js: -------------------------------------------------------------------------------- 1 | const path = require('node:path'); 2 | 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: { config: path.resolve(__dirname, 'tailwind.config.ts') }, 6 | autoprefixer: {}, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/preview-server/src/actions/email-validation/get-code-location-from-ast-element.ts: -------------------------------------------------------------------------------- 1 | import type { HTMLElement } from 'node-html-parser'; 2 | import { getLineAndColumnFromOffset } from '../../utils/get-line-and-column-from-offset'; 3 | 4 | export interface CodeLocation { 5 | line: number; 6 | column: number; 7 | } 8 | 9 | export const getCodeLocationFromAstElement = ( 10 | ast: HTMLElement, 11 | html: string, 12 | ): CodeLocation => { 13 | const [line, column] = getLineAndColumnFromOffset(ast.range[0], html); 14 | return { 15 | line, 16 | column, 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/preview-server/src/actions/email-validation/quick-fetch.ts: -------------------------------------------------------------------------------- 1 | import type { IncomingMessage } from 'node:http'; 2 | import http from 'node:http'; 3 | import https from 'node:https'; 4 | 5 | export const quickFetch = (url: URL) => { 6 | return new Promise((resolve, reject) => { 7 | const caller = url.protocol === 'https:' ? https : http; 8 | caller 9 | .get(url, (res) => { 10 | resolve(res); 11 | }) 12 | .on('error', (error) => reject(error)); 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/preview-server/src/actions/get-emails-directory-metadata-action.ts: -------------------------------------------------------------------------------- 1 | 'use server'; 2 | 3 | import type { EmailsDirectory } from '../utils/get-emails-directory-metadata'; 4 | import { getEmailsDirectoryMetadata } from '../utils/get-emails-directory-metadata'; 5 | 6 | export const getEmailsDirectoryMetadataAction = async ( 7 | absolutePathToEmailsDirectory: string, 8 | keepFileExtensions = false, 9 | isSubDirectory = false, 10 | 11 | baseDirectoryPath = absolutePathToEmailsDirectory, 12 | ): Promise => { 13 | return getEmailsDirectoryMetadata( 14 | absolutePathToEmailsDirectory, 15 | keepFileExtensions, 16 | isSubDirectory, 17 | baseDirectoryPath, 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/preview-server/src/app/env.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 2 | /** ONLY ACCESSIBLE ON THE SERVER */ 3 | export const emailsDirRelativePath = process.env.EMAILS_DIR_RELATIVE_PATH!; 4 | 5 | /** ONLY ACCESSIBLE ON THE SERVER */ 6 | export const userProjectLocation = process.env.USER_PROJECT_LOCATION!; 7 | 8 | /** ONLY ACCESSIBLE ON THE SERVER */ 9 | export const emailsDirectoryAbsolutePath = 10 | process.env.EMAILS_DIR_ABSOLUTE_PATH!; 11 | 12 | export const isBuilding = process.env.NEXT_PUBLIC_IS_BUILDING === 'true'; 13 | 14 | export const isPreviewDevelopment = 15 | process.env.NEXT_PUBLIC_IS_PREVIEW_DEVELOPMENT === 'true'; 16 | -------------------------------------------------------------------------------- /packages/preview-server/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/favicon.ico -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoBold.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoBoldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoBoldItalic.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoHeavy.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoHeavy.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoHeavyItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoHeavyItalic.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoLight.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoLightItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoLightItalic.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoMedium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoMedium.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoMediumItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoMediumItalic.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoRegular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoRegular.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoRegularItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoRegularItalic.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoSemibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoSemibold.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/fonts/SFMono/SFMonoSemiboldItalic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/fonts/SFMono/SFMonoSemiboldItalic.otf -------------------------------------------------------------------------------- /packages/preview-server/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html { 6 | color-scheme: dark; 7 | } 8 | 9 | .popup-open iframe { 10 | pointer-events: none; 11 | } 12 | 13 | nav > div > div > .line { 14 | display: none; 15 | } 16 | -------------------------------------------------------------------------------- /packages/preview-server/src/app/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/app/logo.png -------------------------------------------------------------------------------- /packages/preview-server/src/components/code-snippet.tsx: -------------------------------------------------------------------------------- 1 | const CodeSnippet = ({ children }) => { 2 | return ( 3 | 4 | {children} 5 | 6 | ); 7 | }; 8 | 9 | export default CodeSnippet; 10 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-arrow-down.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import type { IconElement, IconProps } from './icon-base'; 3 | import { IconBase } from './icon-base'; 4 | 5 | export const IconArrowDown = React.forwardRef>( 6 | ({ ...props }, forwardedRef) => ( 7 | 8 | 12 | 13 | ), 14 | ); 15 | 16 | IconArrowDown.displayName = 'IconArrowDown'; 17 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-base.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type IconElement = React.ElementRef<'svg'>; 4 | export type RootProps = React.ComponentPropsWithoutRef<'svg'>; 5 | 6 | export interface IconProps extends RootProps { 7 | size?: number; 8 | } 9 | 10 | export const IconBase = React.forwardRef>( 11 | ({ size = 20, children, ...props }, forwardedRef) => ( 12 | 21 | {children} 22 | 23 | ), 24 | ); 25 | 26 | IconBase.displayName = 'IconBase'; 27 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { cn } from '../../utils'; 3 | 4 | export type IconButtonProps = React.ComponentPropsWithoutRef<'button'>; 5 | 6 | export const IconButton = React.forwardRef< 7 | HTMLButtonElement, 8 | Readonly 9 | >(({ children, className, ...props }, forwardedRef) => ( 10 | 21 | )); 22 | 23 | IconButton.displayName = 'IconButton'; 24 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-check.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import type { IconElement, IconProps } from './icon-base'; 3 | import { IconBase } from './icon-base'; 4 | 5 | export const IconCheck = React.forwardRef>( 6 | ({ ...props }, forwardedRef) => ( 7 | 8 | 15 | 16 | ), 17 | ); 18 | 19 | IconCheck.displayName = 'IconCheck'; 20 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-download.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import type { IconElement, IconProps } from './icon-base'; 3 | import { IconBase } from './icon-base'; 4 | 5 | export const IconDownload = React.forwardRef>( 6 | ({ ...props }, forwardedRef) => ( 7 | 8 | 15 | 16 | ), 17 | ); 18 | 19 | IconDownload.displayName = 'IconDownload'; 20 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-email.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import type { IconElement, IconProps } from './icon-base'; 3 | import { IconBase } from './icon-base'; 4 | 5 | export const IconEmail = React.forwardRef>( 6 | (props, forwardedRef) => { 7 | return ( 8 | 9 | 13 | 14 | ); 15 | }, 16 | ); 17 | 18 | IconEmail.displayName = 'IconEmail'; 19 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-image.tsx: -------------------------------------------------------------------------------- 1 | import { forwardRef } from 'react'; 2 | import type { IconElement, IconProps } from './icon-base'; 3 | import { IconBase } from './icon-base'; 4 | 5 | export const IconImage = forwardRef((props, ref) => ( 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | )); 20 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/icons/icon-source.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import type { IconElement, IconProps } from './icon-base'; 3 | import { IconBase } from './icon-base'; 4 | 5 | export const IconSource = React.forwardRef>( 6 | ({ ...props }, forwardedRef) => ( 7 | 8 | 15 | 16 | ), 17 | ); 18 | 19 | IconSource.displayName = 'IconSource'; 20 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './button'; 2 | export * from './code'; 3 | export * from './heading'; 4 | export * from './logo'; 5 | export * from './sidebar'; 6 | export * from './text'; 7 | export * from './topbar'; 8 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/sidebar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './sidebar'; 2 | -------------------------------------------------------------------------------- /packages/preview-server/src/components/toolbar/results-table.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/preview-server/src/components/toolbar/results-table.tsx -------------------------------------------------------------------------------- /packages/preview-server/src/components/tooltip.tsx: -------------------------------------------------------------------------------- 1 | import * as TooltipPrimitive from '@radix-ui/react-tooltip'; 2 | import type * as React from 'react'; 3 | import { TooltipContent } from './tooltip-content'; 4 | 5 | type RootProps = React.ComponentPropsWithoutRef; 6 | 7 | export type TooltipProps = RootProps; 8 | 9 | export const TooltipRoot: React.FC> = ({ 10 | children, 11 | ...props 12 | }) => {children}; 13 | 14 | export const Tooltip = Object.assign(TooltipRoot, { 15 | Arrow: TooltipPrimitive.TooltipArrow, 16 | Provider: TooltipPrimitive.TooltipProvider, 17 | Content: TooltipContent, 18 | Trigger: TooltipPrimitive.TooltipTrigger, 19 | }); 20 | -------------------------------------------------------------------------------- /packages/preview-server/src/hooks/use-fragment-identifier.ts: -------------------------------------------------------------------------------- 1 | import { usePathname, useSearchParams } from 'next/navigation'; 2 | import { useEffect, useState } from 'react'; 3 | 4 | export const useFragmentIdentifier = () => { 5 | const pathname = usePathname(); 6 | const searchParams = useSearchParams(); 7 | const [fragmentIdentifier, setFragmentIdentifier] = useState(); 8 | 9 | useEffect(() => { 10 | setFragmentIdentifier(global.location?.hash); 11 | }, [pathname, searchParams]); 12 | 13 | return fragmentIdentifier; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/caniemail/ast/__snapshots__/get-used-style-properties.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`getUsedStyleProperties() 1`] = ` 4 | [ 5 | { 6 | "location": SourceLocation { 7 | "end": Position { 8 | "column": 21, 9 | "index": 91, 10 | "line": 5, 11 | }, 12 | "filename": undefined, 13 | "identifierName": undefined, 14 | "start": Position { 15 | "column": 2, 16 | "index": 72, 17 | "line": 5, 18 | }, 19 | }, 20 | "name": "borderRadius", 21 | "value": "5px", 22 | }, 23 | ] 24 | `; 25 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/caniemail/ast/get-object-variables.spec.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '@babel/parser'; 2 | import { getObjectVariables } from './get-object-variables'; 3 | 4 | test('getObjectVariables()', () => { 5 | const reactCode = ` 6 | 7 | 8 | const buttonStyle = { 9 | borderRadius: '5px', 10 | }; 11 | `; 12 | const ast = parse(reactCode, { 13 | strictMode: false, 14 | errorRecovery: true, 15 | sourceType: 'unambiguous', 16 | plugins: ['jsx', 'typescript', 'decorators'], 17 | }); 18 | expect(getObjectVariables(ast)).toMatchSnapshot(); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/caniemail/get-css-property-with-value.ts: -------------------------------------------------------------------------------- 1 | const propertyRegex = 2 | /(?[a-z-]+)\s*:\s*(?[a-zA-Z\-0-9()+*/_ ]+)/; 3 | 4 | export const getCssPropertyWithValue = (title: string) => { 5 | const match = propertyRegex.exec(title.trim()); 6 | if (match) { 7 | const [_full, propertyName, propertyValue] = match; 8 | return { 9 | name: propertyName!, 10 | value: propertyValue!, 11 | }; 12 | } 13 | return undefined; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/caniemail/get-css-unit.ts: -------------------------------------------------------------------------------- 1 | export const getCssUnit = (title: string) => { 2 | return title.endsWith(' unit') ? title.replace(' unit', '') : undefined; 3 | }; 4 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/caniemail/get-element-attributes.ts: -------------------------------------------------------------------------------- 1 | export function getElementAttributes(title: string) { 2 | if (title.endsWith(' attribute')) { 3 | return [title.replace(' attribute', '')]; 4 | } 5 | 6 | return []; 7 | } 8 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/caniemail/get-element-names.ts: -------------------------------------------------------------------------------- 1 | export const getElementNames = (title: string, keywords: string | null) => { 2 | const match = /<(?[^>]*)> element/.exec(title); 3 | if (match) { 4 | const [_full, elementName] = match; 5 | 6 | if (elementName) { 7 | return [elementName.toLowerCase()]; 8 | } 9 | } 10 | 11 | if (keywords !== null && keywords.length > 0) { 12 | return keywords 13 | .toLowerCase() 14 | .split(/\s*,\s*/) 15 | .map((piece) => piece.trim()); 16 | } 17 | 18 | if (title.split(',').length > 1) { 19 | return title 20 | .toLowerCase() 21 | .split(/\s*,\s*/) 22 | .map((piece) => piece.trim()); 23 | } 24 | 25 | return []; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/caniemail/tailwind/setup-tailwind-context.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'tailwindcss/lib/lib/setupContextUtils'; 2 | import resolveConfig from 'tailwindcss/resolveConfig'; 3 | import type { TailwindConfig } from './get-tailwind-config'; 4 | 5 | export const setupTailwindContext = (config: TailwindConfig) => { 6 | return createContext( 7 | resolveConfig({ 8 | ...config, 9 | content: [], 10 | corePlugins: { 11 | preflight: false, 12 | }, 13 | }), 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/cn.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export const cn = (...inputs: ClassValue[]) => { 5 | return twMerge(clsx(inputs)); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const tabTransition = { 2 | type: 'spring', 3 | stiffness: 2000, 4 | damping: 80, 5 | mass: 1, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/copy-text-to-clipboard.ts: -------------------------------------------------------------------------------- 1 | export const copyTextToClipboard = async (text: string) => { 2 | try { 3 | await navigator.clipboard.writeText(text); 4 | } catch { 5 | throw new Error('Not able to copy'); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/esbuild/escape-string-for-regex.ts: -------------------------------------------------------------------------------- 1 | export function escapeStringForRegex(string: string) { 2 | return string.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d'); 3 | } 4 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/get-line-and-column-from-offset.spec.ts: -------------------------------------------------------------------------------- 1 | import { getLineAndColumnFromOffset } from './get-line-and-column-from-offset'; 2 | 3 | test('getLineAndColumnFromOffset()', () => { 4 | const content = `export default function MyEmail() { 5 | return
6 | inside the div, should also stay unchanged 7 |
; 8 | }`; 9 | const offset = content.indexOf('className'); 10 | expect(getLineAndColumnFromOffset(offset, content)).toEqual([2, 15]); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/get-line-and-column-from-offset.ts: -------------------------------------------------------------------------------- 1 | export const getLineAndColumnFromOffset = ( 2 | offset: number, 3 | content: string, 4 | ): [line: number, column: number] => { 5 | const lineBreaks = [...content.slice(0, offset).matchAll(/\n|\r|\r\n/g)]; 6 | 7 | const line = lineBreaks.length + 1; 8 | const column = offset - (lineBreaks[lineBreaks.length - 1]?.index ?? 0); 9 | 10 | return [line, column]; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cn'; 2 | export * from './copy-text-to-clipboard'; 3 | export * from './language-map'; 4 | export * from './sanitize'; 5 | export * from './types/as'; 6 | export * from './unreachable'; 7 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/language-map.ts: -------------------------------------------------------------------------------- 1 | const languageMap = { 2 | jsx: 'React', 3 | markup: 'HTML', 4 | markdown: 'Plain Text', 5 | }; 6 | 7 | export default languageMap; 8 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/load-stream.ts: -------------------------------------------------------------------------------- 1 | export async function* loadStream(stream: ReadableStream) { 2 | const reader = stream.getReader(); 3 | try { 4 | while (true) { 5 | const { value, done } = await reader.read(); 6 | if (done) { 7 | break; 8 | } 9 | 10 | yield value; 11 | } 12 | } finally { 13 | reader.releaseLock(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/register-spinner-autostopping.ts: -------------------------------------------------------------------------------- 1 | import logSymbols from 'log-symbols'; 2 | import type { Ora } from 'ora'; 3 | 4 | const spinners = new Set(); 5 | 6 | process.on('SIGINT', () => { 7 | spinners.forEach((spinner) => { 8 | if (spinner.isSpinning) { 9 | spinner.stop(); 10 | } 11 | }); 12 | }); 13 | 14 | process.on('exit', (code) => { 15 | if (code !== 0) { 16 | spinners.forEach((spinner) => { 17 | if (spinner.isSpinning) { 18 | spinner.stopAndPersist({ 19 | symbol: logSymbols.error, 20 | }); 21 | } 22 | }); 23 | } 24 | }); 25 | 26 | export const registerSpinnerAutostopping = (spinner: Ora) => { 27 | spinners.add(spinner); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/sanitize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Sanitizes text by replacing underscores and hyphens with spaces 3 | */ 4 | export const sanitize = (text: string): string => { 5 | return text.replace(/[_-]/g, ' '); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/testing/js-email-export-default.js: -------------------------------------------------------------------------------- 1 | // A JavaScript email component with ES6 export default 2 | import { Button, Html } from '@react-email/components'; 3 | 4 | function Email() { 5 | return ( 6 | 7 | 13 | 14 | ); 15 | } 16 | 17 | export default Email; 18 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/testing/js-email-test.js: -------------------------------------------------------------------------------- 1 | // A simple JavaScript email component 2 | const _React = require('react'); 3 | const { Html, Button } = require('@react-email/components'); 4 | 5 | function Email() { 6 | return ( 7 | 8 | 14 | 15 | ); 16 | } 17 | 18 | module.exports = Email; 19 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/testing/request-response-email.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | const _req = new Request('https://react.email'); 3 | const _res = new Response('{}'); 4 | 5 | const Email = () => { 6 | return
; 7 | }; 8 | 9 | export default Email; 10 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/types/as.ts: -------------------------------------------------------------------------------- 1 | export type As< 2 | DefaultTag extends React.ElementType, 3 | T1 extends React.ElementType, 4 | T2 extends React.ElementType = T1, 5 | T3 extends React.ElementType = T1, 6 | T4 extends React.ElementType = T1, 7 | T5 extends React.ElementType = T1, 8 | > = 9 | | (React.ComponentPropsWithRef & { 10 | as?: DefaultTag; 11 | }) 12 | | (React.ComponentPropsWithRef & { 13 | as: T1; 14 | }) 15 | | (React.ComponentPropsWithRef & { 16 | as: T2; 17 | }) 18 | | (React.ComponentPropsWithRef & { 19 | as: T3; 20 | }) 21 | | (React.ComponentPropsWithRef & { 22 | as: T4; 23 | }) 24 | | (React.ComponentPropsWithRef & { 25 | as: T5; 26 | }); 27 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/types/email-template.ts: -------------------------------------------------------------------------------- 1 | export interface EmailTemplate { 2 | (props: Record | Record): React.ReactNode; 3 | PreviewProps?: Record; 4 | } 5 | 6 | export const isEmailTemplate = (val: unknown): val is EmailTemplate => { 7 | return typeof val === 'function'; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/types/error-object.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An object that mimics the structure of the Error class, 3 | * we just can't use the Error class here because server actions can't 4 | * return classes 5 | */ 6 | export interface ErrorObject { 7 | name: string; 8 | stack: string | undefined; 9 | cause?: unknown; 10 | message: string; 11 | } 12 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/types/hot-reload-change.ts: -------------------------------------------------------------------------------- 1 | export interface HotReloadChange { 2 | filename: string; 3 | event: 4 | | 'all' 5 | | 'ready' 6 | | 'add' 7 | | 'change' 8 | | 'addDir' 9 | | 'unlink' 10 | | 'unlinkDir' 11 | | 'raw' 12 | | 'error'; 13 | } 14 | -------------------------------------------------------------------------------- /packages/preview-server/src/utils/unreachable.ts: -------------------------------------------------------------------------------- 1 | export const unreachable = ( 2 | condition: string | Record, 3 | message = `Entered unreachable code. Received '${ 4 | typeof condition === 'string' ? condition : JSON.stringify(condition) 5 | }'.`, 6 | ): never => { 7 | throw new TypeError(message); 8 | }; 9 | -------------------------------------------------------------------------------- /packages/preview-server/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | environment: 'happy-dom', 7 | }, 8 | esbuild: { 9 | tsconfigRaw: { 10 | compilerOptions: { 11 | jsx: 'react-jsx', 12 | }, 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/preview/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './preview'; 2 | -------------------------------------------------------------------------------- /packages/preview/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/react-email/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | cli 4 | !src/cli 5 | 6 | # for testing 7 | .for-dependency-graph.ts 8 | static 9 | -------------------------------------------------------------------------------- /packages/react-email/.npmignore: -------------------------------------------------------------------------------- 1 | .react-email 2 | ./emails 3 | ./emails/static 4 | node_modules 5 | .turbo 6 | src/cli 7 | tsup.config.ts 8 | -------------------------------------------------------------------------------- /packages/react-email/src/commands/.npmignore: -------------------------------------------------------------------------------- 1 | testing 2 | -------------------------------------------------------------------------------- /packages/react-email/src/commands/testing/.gitignore: -------------------------------------------------------------------------------- 1 | # for the `email export` command 2 | out 3 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/esbuild/escape-string-for-regex.ts: -------------------------------------------------------------------------------- 1 | export function escapeStringForRegex(string: string) { 2 | return string.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d'); 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './preview/index.js'; 2 | export * from './tree.js'; 3 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/packageJson.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error Typescript doesn't want to allow this, but it's fine since we're using tsup 2 | import packageJson from '../../package.json'; 3 | 4 | export { packageJson }; 5 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/preview/get-env-variables-for-preview-app.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { isDev } from './start-dev-server.js'; 3 | 4 | export const getEnvVariablesForPreviewApp = ( 5 | relativePathToEmailsDirectory: string, 6 | cwd: string, 7 | ) => { 8 | return { 9 | EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory, 10 | EMAILS_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToEmailsDirectory), 11 | USER_PROJECT_LOCATION: cwd, 12 | NEXT_PUBLIC_IS_PREVIEW_DEVELOPMENT: isDev ? 'true' : 'false', 13 | } as const; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/preview/hot-reloading/resolve-path-aliases.spec.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { resolvePathAliases } from './resolve-path-aliases.js'; 3 | 4 | test('resolveImports()', async () => { 5 | expect( 6 | resolvePathAliases( 7 | ['@/some-file'], 8 | path.resolve(import.meta.dirname, './test'), 9 | ), 10 | ).toEqual(['./some-file']); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/preview/hot-reloading/test/some-file.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/react-email/src/utils/preview/hot-reloading/test/some-file.ts -------------------------------------------------------------------------------- /packages/react-email/src/utils/preview/hot-reloading/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | }, 7 | "include": ["**/*.ts", "**/*.tsx"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/preview/index.ts: -------------------------------------------------------------------------------- 1 | export * from './hot-reloading/setup-hot-reloading.js'; 2 | export * from './start-dev-server.js'; 3 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/register-spinner-autostopping.ts: -------------------------------------------------------------------------------- 1 | import logSymbols from 'log-symbols'; 2 | import type { Ora } from 'ora'; 3 | 4 | const spinners = new Set(); 5 | 6 | process.on('SIGINT', () => { 7 | spinners.forEach((spinner) => { 8 | if (spinner.isSpinning) { 9 | spinner.stop(); 10 | } 11 | }); 12 | }); 13 | 14 | process.on('exit', (code) => { 15 | if (code !== 0) { 16 | spinners.forEach((spinner) => { 17 | if (spinner.isSpinning) { 18 | spinner.stopAndPersist({ 19 | symbol: logSymbols.error, 20 | }); 21 | } 22 | }); 23 | } 24 | }); 25 | 26 | export const registerSpinnerAutostopping = (spinner: Ora) => { 27 | spinners.add(spinner); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/tree.spec.ts: -------------------------------------------------------------------------------- 1 | import { tree } from './tree.js'; 2 | 3 | test('tree(__dirname, 2)', async () => { 4 | expect(await tree(__dirname, 2)).toMatchSnapshot(); 5 | }); 6 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/types/hot-reload-change.ts: -------------------------------------------------------------------------------- 1 | import type { HotReloadEvent } from './hot-reload-event.js'; 2 | 3 | export interface HotReloadChange { 4 | filename: string; 5 | event: HotReloadEvent; 6 | } 7 | -------------------------------------------------------------------------------- /packages/react-email/src/utils/types/hot-reload-event.ts: -------------------------------------------------------------------------------- 1 | import type { EventName } from 'chokidar/handler.js'; 2 | 3 | export type HotReloadEvent = EventName; 4 | -------------------------------------------------------------------------------- /packages/react-email/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig({ 4 | dts: false, 5 | entry: ['./src/index.ts'], 6 | format: ['esm'], 7 | outDir: 'dist', 8 | }); 9 | -------------------------------------------------------------------------------- /packages/react-email/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | globals: true, 6 | environment: 'happy-dom', 7 | }, 8 | esbuild: { 9 | tsconfigRaw: { 10 | compilerOptions: { 11 | jsx: 'react-jsx', 12 | }, 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/render/src/browser/index.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from '../shared/options'; 2 | import { render } from './render'; 3 | 4 | /** 5 | * @deprecated use {@link render} 6 | */ 7 | export const renderAsync = (element: React.ReactElement, options?: Options) => { 8 | return render(element, options); 9 | }; 10 | 11 | export * from '../shared/options'; 12 | export * from '../shared/plain-text-selectors'; 13 | export * from '../shared/utils/pretty'; 14 | export * from './render'; 15 | -------------------------------------------------------------------------------- /packages/render/src/node/index.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from '../shared/options'; 2 | import { render } from './render'; 3 | 4 | /** 5 | * @deprecated use {@link render} 6 | */ 7 | export const renderAsync = (element: React.ReactElement, options?: Options) => { 8 | return render(element, options); 9 | }; 10 | 11 | export * from '../shared/options'; 12 | export * from '../shared/plain-text-selectors'; 13 | export * from '../shared/utils/pretty'; 14 | export * from './render'; 15 | -------------------------------------------------------------------------------- /packages/render/src/react-internals.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-dom/server.browser" { 2 | export * from "react-dom/server"; 3 | } 4 | -------------------------------------------------------------------------------- /packages/render/src/shared/options.ts: -------------------------------------------------------------------------------- 1 | import type { HtmlToTextOptions } from 'html-to-text'; 2 | import type { pretty } from './utils/pretty'; 3 | 4 | export type Options = { 5 | /** 6 | * @deprecated use {@link pretty} instead 7 | */ 8 | pretty?: boolean; 9 | } & ( 10 | | { 11 | plainText?: false; 12 | } 13 | | { 14 | plainText?: true; 15 | /** 16 | * These are options you can pass down directly to the library we use for 17 | * converting the rendered email's HTML into plain text. 18 | * 19 | * @see https://github.com/html-to-text/node-html-to-text 20 | */ 21 | htmlToTextOptions?: HtmlToTextOptions; 22 | } 23 | ); 24 | -------------------------------------------------------------------------------- /packages/render/src/shared/plain-text-selectors.ts: -------------------------------------------------------------------------------- 1 | import type { SelectorDefinition } from 'html-to-text'; 2 | 3 | export const plainTextSelectors: SelectorDefinition[] = [ 4 | { selector: 'img', format: 'skip' }, 5 | { selector: '[data-skip-in-text=true]', format: 'skip' }, 6 | { 7 | selector: 'a', 8 | options: { linkBrackets: false }, 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /packages/render/src/shared/utils/pretty.spec.ts: -------------------------------------------------------------------------------- 1 | import { promises as fs } from 'node:fs'; 2 | import path from 'node:path'; 3 | import { pretty } from './pretty'; 4 | 5 | describe('pretty', () => { 6 | it("should prettify Preview component's complex characters correctly", async () => { 7 | const stripeHTML = await fs.readFile( 8 | path.resolve(__dirname, './stripe-email.html'), 9 | 'utf8', 10 | ); 11 | 12 | expect(await pretty(stripeHTML)).toMatchSnapshot(); 13 | }); 14 | 15 | test('if mso syntax does not wrap', async () => { 16 | expect( 17 | await pretty( 18 | ``, 19 | ), 20 | ).toMatchSnapshot(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/render/src/shared/utils/preview.tsx: -------------------------------------------------------------------------------- 1 | export const Preview: React.FC = () => ( 2 | <> 3 |
This should be hidden from plain text
4 |

This should be rendered in plain text

5 | 6 | ); 7 | -------------------------------------------------------------------------------- /packages/render/src/shared/utils/template.tsx: -------------------------------------------------------------------------------- 1 | import type * as React from 'react'; 2 | 3 | interface TemplateProps { 4 | firstName: string; 5 | } 6 | 7 | export const Template: React.FC> = ({ firstName }) => ( 8 | <> 9 |

Welcome, {firstName}!

10 | test 11 |

Thanks for trying our product. We're thrilled to have you on board!

12 | 13 | ); 14 | -------------------------------------------------------------------------------- /packages/render/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/render/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup'; 2 | 3 | export default defineConfig([ 4 | { 5 | dts: true, 6 | entry: ['./src/node/index.ts'], 7 | outDir: './dist/node', 8 | format: ['cjs', 'esm'], 9 | }, 10 | { 11 | dts: true, 12 | entry: ['./src/browser/index.ts'], 13 | outDir: './dist/browser', 14 | format: ['cjs', 'esm'], 15 | }, 16 | ]); 17 | -------------------------------------------------------------------------------- /packages/row/src/__snapshots__/row.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[` component > renders correctly 1`] = `"
"`; 4 | -------------------------------------------------------------------------------- /packages/row/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './row'; 2 | -------------------------------------------------------------------------------- /packages/row/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/section/src/__snapshots__/section.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`
component > renders correctly 1`] = `"
Lorem ipsum
"`; 4 | -------------------------------------------------------------------------------- /packages/section/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './section'; 2 | -------------------------------------------------------------------------------- /packages/section/src/section.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type SectionProps = Readonly>; 4 | 5 | export const Section = React.forwardRef( 6 | ({ children, style, ...props }, ref) => { 7 | return ( 8 | 19 | 20 | 21 | 22 | 23 | 24 |
{children}
25 | ); 26 | }, 27 | ); 28 | 29 | Section.displayName = 'Section'; 30 | -------------------------------------------------------------------------------- /packages/section/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/tailwind/copy-tailwind-types.mjs: -------------------------------------------------------------------------------- 1 | import { promises as fs } from 'node:fs'; 2 | import path from 'node:path'; 3 | import url from 'node:url'; 4 | 5 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); 6 | 7 | await fs.cp( 8 | path.resolve(__dirname, './node_modules/tailwindcss/types'), 9 | path.resolve(__dirname, './dist/tailwindcss'), 10 | { 11 | recursive: true, 12 | }, 13 | ); 14 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .yalc 7 | yalc.lock 8 | .pnp.js 9 | yalc.lock 10 | .yarn/install-state.gz 11 | 12 | # testing 13 | /coverage 14 | 15 | # next.js 16 | /.next/ 17 | /out/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | 26 | # debug 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/README.md: -------------------------------------------------------------------------------- 1 | This is a NextJS project bootstrapped with `create-next-app@latest` that contains a Tailwind 2 | email using most of the features we provide that will be auto-built on testing to 3 | make sure this new release works properly with the latest version of NextJS when being built. 4 | 5 | We do this testing so that things are more reliable and this couldn't be done using pnpm 6 | workspaces as they link code instead of actually copying the content into node_modules. 7 | The solution we use to copy the code in a way that mimics the actual `npm install @react-email/tailwind` is `yalc`. 8 | 9 | Se [the test file](../_tests/nextjs.spec.ts) for more info. 10 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | compress: false, 4 | swcMinify: false, 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-with-tailwind", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "preinstall": "yalc add @react-email/tailwind" 10 | }, 11 | "dependencies": { 12 | "@react-email/components": "0.0.36", 13 | "@react-email/tailwind": "file:.yalc/@react-email/tailwind", 14 | "next": "^15", 15 | "react": "^19", 16 | "react-dom": "^19" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^22.0.0", 20 | "@types/react": "^19", 21 | "@types/react-dom": "^19", 22 | "typescript": "^5", 23 | "yalc": "1.0.0-pre.53" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resend/react-email/e3fc0c97ce100aaadbf7511dde9aec8e92e77534/packages/tailwind/integrations/nextjs/src/app/favicon.ico -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next'; 2 | import { Inter } from 'next/font/google'; 3 | 4 | const inter = Inter({ subsets: ['latin'] }); 5 | 6 | export const metadata: Metadata = { 7 | title: 'Create Next App', 8 | description: 'Generated by create next app', 9 | }; 10 | 11 | const RootLayout = ({ 12 | children, 13 | }: Readonly<{ 14 | children: React.ReactNode; 15 | }>) => { 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | export default RootLayout; 24 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/components'; 2 | import { VercelInviteUserEmail } from '../../emails/vercel-invite-user'; 3 | 4 | export default async function Home() { 5 | const emailHtml = await render(); 6 | return
; 7 | } 8 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/nextjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | }, 23 | "target": "ES2017" 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | .yalc 13 | yalc.lock 14 | dist-ssr 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/README.md: -------------------------------------------------------------------------------- 1 | This is a Vite React project bootstrapped with `npm create vite@latest` that contains a Tailwind 2 | email using most of the features we provide that will be auto-built on testing to 3 | make sure this new release works properly with the latest version of Vite when being built. 4 | 5 | We do this testing so that things are more reliable before we get to publishing. This 6 | couldn't be done using pnpm workspaces as they link code instead of actually copying the content into node_modules. 7 | The solution we use to copy the code, in a way that mimics the actual `npm install @react-email/tailwind`, is `yalc`. 8 | 9 | Se [the test file](../_tests/vite.spec.ts) for more info. 10 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-with-tailwind", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preinstall": "yalc add @react-email/tailwind", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@react-email/components": "0.0.36", 14 | "@react-email/tailwind": "file:.yalc/@react-email/tailwind", 15 | "react": "^19", 16 | "react-dom": "^19" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^19", 20 | "@types/react-dom": "^19", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "typescript": "^5.2.2", 23 | "vite": "^6.3.2", 24 | "yalc": "1.0.0-pre.53" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/components'; 2 | import { VercelInviteUserEmail } from '../emails/vercel-invite-user'; 3 | 4 | function App() { 5 | const emailHtml = render(); 6 | 7 | return
; 8 | } 9 | 10 | export default App; 11 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App.tsx'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root')!).render( 6 | 7 | 8 | , 9 | ); 10 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/tailwind/integrations/vite/vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react'; 2 | import { defineConfig } from 'vite'; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }); 8 | -------------------------------------------------------------------------------- /packages/tailwind/src/hooks/__snapshots__/use-tailwind.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`useTailwind() 1`] = ` 4 | ".text-red-500 { 5 | color: rgb(239 68 68 / 1) 6 | } 7 | @media (min-width: 640px) { 8 | .sm\\:bg-blue-300 { 9 | background-color: rgb(147 197 253 / 1) 10 | } 11 | } 12 | .bg-slate-900 { 13 | background-color: rgb(15 23 42 / 1) 14 | } 15 | " 16 | `; 17 | -------------------------------------------------------------------------------- /packages/tailwind/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './tailwind'; 2 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/__snapshots__/quick-safe-render-to-string.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`quick safe render to string 1`] = `"

"`; 4 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/compatibility/convert-css-property-to-react-property.ts: -------------------------------------------------------------------------------- 1 | import { fromDashCaseToCamelCase } from '../text/from-dash-case-to-camel-case'; 2 | 3 | export const convertCssPropertyToReactProperty = (prop: string) => { 4 | const modifiedProp = prop.toLowerCase(); 5 | 6 | if (modifiedProp.startsWith('--')) { 7 | return modifiedProp; 8 | } 9 | 10 | if (modifiedProp.startsWith('-ms-')) { 11 | return fromDashCaseToCamelCase(modifiedProp.slice(1)); 12 | } 13 | 14 | return fromDashCaseToCamelCase(modifiedProp); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/compatibility/escape-class-name.spec.ts: -------------------------------------------------------------------------------- 1 | import { escapeClassName } from './escape-class-name'; 2 | 3 | describe('escapeClassName function', () => { 4 | it('should escape the first character properly', () => { 5 | expect(escapeClassName('[min-height-')).toBe('\\[min-height-'); 6 | }); 7 | 8 | it('should not escape an already escaped first-character', () => { 9 | expect(escapeClassName('\\[min-height-')).toBe('\\[min-height-'); 10 | }); 11 | 12 | it('should escape `min-height-[calc(25px+100%-20%*2/4)]` correctly', () => { 13 | expect(escapeClassName('min-height-[calc(25px+100%-20%*2/4)]')).toBe( 14 | 'min-height-\\[calc\\(25px\\+100\\%-20\\%\\*2\\/4\\)\\]', 15 | ); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/compatibility/escape-class-name.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Escapes all characters that may not be accepted on 3 | * CSS selectors by using the regex "[^a-zA-Z0-9\-_]". 4 | * 5 | * Also does a bit more trickery to avoid escaping already 6 | * escaped characters. 7 | */ 8 | export const escapeClassName = (className: string) => { 9 | return className.replace( 10 | /* we need this look ahead capturing group to avoid using negative look behinds */ 11 | /([^\\]|^)(?=([^a-zA-Z0-9\-_]))/g, 12 | (match, prefixCharacter: string, characterToEscape: string) => { 13 | if (prefixCharacter === '' && characterToEscape === '\\') return match; 14 | 15 | return `${prefixCharacter}\\`; 16 | }, 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/compatibility/sanitize-class-name.spec.ts: -------------------------------------------------------------------------------- 1 | import { sanitizeClassName } from './sanitize-class-name'; 2 | 3 | test('sanitizeClassName', () => { 4 | expect(sanitizeClassName('min-height-[calc(25px+100%-20%*2/4)]')).toBe( 5 | 'min-height-calc25pxplus100pc-20pc_2_4', 6 | ); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/compatibility/unescape-class.ts: -------------------------------------------------------------------------------- 1 | export function unescapeClass(singleClass: string) { 2 | return singleClass.replaceAll(/\\[0-9]|\\/g, ''); 3 | } 4 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/css/make-inline-styles-for.spec.ts: -------------------------------------------------------------------------------- 1 | import { setupTailwind } from '../tailwindcss/setup-tailwind'; 2 | import { makeInlineStylesFor } from './make-inline-styles-for'; 3 | 4 | test('makeInlineStylesFor()', () => { 5 | const tailwind = setupTailwind({}); 6 | 7 | const className = 8 | 'bg-red-500 sm:bg-blue-300 w-full md:max-w-[400px] my-custom-class'; 9 | const tailwindStyles = tailwind.generateRootForClasses(className.split(' ')); 10 | 11 | expect(makeInlineStylesFor(className, tailwindStyles)).toEqual({ 12 | styles: { backgroundColor: 'rgb(239 68 68 / 1)', width: '100%' }, 13 | residualClassName: 'sm:bg-blue-300 md:max-w-[400px] my-custom-class', 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/css/remove-if-empty-recursively.ts: -------------------------------------------------------------------------------- 1 | import type { Container, Document } from 'postcss'; 2 | 3 | export const removeIfEmptyRecursively = (node: Container | Document) => { 4 | if (node.first === undefined) { 5 | const parent = node.parent; 6 | if (parent) { 7 | node.remove(); 8 | removeIfEmptyRecursively(parent); 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/css/remove-rule-duplicates-from-root.ts: -------------------------------------------------------------------------------- 1 | import type { Root } from 'postcss'; 2 | import { removeIfEmptyRecursively } from './remove-if-empty-recursively'; 3 | 4 | export const removeRuleDuplicatesFromRoot = (root: Root) => { 5 | root.walkRules((rule) => { 6 | root.walkRules(rule.selector, (duplicateRule) => { 7 | if (duplicateRule === rule) return; 8 | 9 | const parent = duplicateRule.parent; 10 | duplicateRule.remove(); 11 | if (parent) { 12 | removeIfEmptyRecursively(parent); 13 | } 14 | }); 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/react/is-component.ts: -------------------------------------------------------------------------------- 1 | export const isComponent = ( 2 | element: React.ReactElement, 3 | ): element is React.ReactElement> => { 4 | return ( 5 | typeof element.type === 'function' || 6 | // @ts-expect-error - we know this is a component that may have a render function 7 | element.type.render !== undefined 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/tailwindcss/__snapshots__/setup-tailwind.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`tailwind's generateRootForClasses() 1`] = ` 4 | ".text-red-500 { 5 | color: rgb(239 68 68 / 1) 6 | } 7 | @media (min-width: 640px) { 8 | .sm\\:bg-blue-300 { 9 | background-color: rgb(147 197 253 / 1) 10 | } 11 | } 12 | .bg-slate-900 { 13 | background-color: rgb(15 23 42 / 1) 14 | } 15 | " 16 | `; 17 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/tailwindcss/setup-tailwind-context.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'tailwindcss/lib/lib/setupContextUtils'; 2 | import resolveConfig from 'tailwindcss/resolveConfig'; 3 | import type { TailwindConfig } from '../../tailwind'; 4 | 5 | export const setupTailwindContext = (config: TailwindConfig) => { 6 | return createContext( 7 | resolveConfig({ 8 | ...config, 9 | content: [], 10 | corePlugins: { 11 | preflight: false, 12 | }, 13 | }), 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/tailwindcss/setup-tailwind.spec.ts: -------------------------------------------------------------------------------- 1 | import { setupTailwind } from './setup-tailwind'; 2 | 3 | test("tailwind's generateRootForClasses()", () => { 4 | const tailwind = setupTailwind({}); 5 | 6 | expect( 7 | tailwind 8 | .generateRootForClasses([ 9 | 'text-red-500', 10 | 'sm:bg-blue-300', 11 | 'bg-slate-900', 12 | ]) 13 | .toString(), 14 | ).toMatchSnapshot(); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/tailwind/src/utils/text/from-dash-case-to-camel-case.ts: -------------------------------------------------------------------------------- 1 | export const fromDashCaseToCamelCase = (text: string) => { 2 | return text.replace(/-(\w|$)/g, (_, p1: string) => p1.toUpperCase()); 3 | }; 4 | -------------------------------------------------------------------------------- /packages/tailwind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "tsconfig/react-library.json", 4 | "include": ["."], 5 | "exclude": [ 6 | "dist", 7 | "build", 8 | "node_modules", 9 | "integrations/vite", 10 | "integrations/nextjs" 11 | ], 12 | "compilerOptions": { 13 | "strict": true, 14 | "noEmit": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/tailwind/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | test: { 6 | globals: true, 7 | environment: 'happy-dom', 8 | }, 9 | server: { 10 | watch: { 11 | ignored: [path.resolve(__dirname, './integrations/**/*')], 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /packages/text/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './text'; 2 | -------------------------------------------------------------------------------- /packages/text/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "inlineSources": false, 11 | "isolatedModules": true, 12 | "moduleResolution": "node", 13 | "noUnusedLocals": false, 14 | "noUnusedParameters": false, 15 | "preserveWatchOutput": true, 16 | "skipLibCheck": true, 17 | "strict": true, 18 | "strictNullChecks": true 19 | }, 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/tsconfig/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "plugins": [{ "name": "next" }], 7 | "allowJs": true, 8 | "declaration": false, 9 | "declarationMap": false, 10 | "incremental": true, 11 | "jsx": "preserve", 12 | "lib": ["dom", "dom.iterable", "esnext"], 13 | "module": "esnext", 14 | "noEmit": true, 15 | "resolveJsonModule": true, 16 | "strict": false, 17 | "target": "es5" 18 | }, 19 | "include": ["src", "next-env.d.ts"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "publishConfig": { 7 | "access": "public" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/tsconfig/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx", 7 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 8 | "module": "ESNext", 9 | "target": "es6", 10 | "types": ["vitest/globals"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | - "benchmarks/*" 5 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended"], 4 | "ignorePaths": ["benchmarks"], 5 | "schedule": ["before 3am on the first day of the month"], 6 | "updateNotScheduled": false, 7 | "rebaseWhen": "conflicted" 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/base.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "types": ["vitest/globals"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { defineConfig } from 'vitest/config'; 4 | 5 | export default defineConfig({ 6 | test: { 7 | globals: true, 8 | environment: 'happy-dom', 9 | }, 10 | }); 11 | --------------------------------------------------------------------------------