メールの HTML を React + TypeScript + TailwindCSS で書く
React コンポーネントをサーバーサイドレンダリングすることで、メールの HTML を React で書くことができる。
フロントエンドで React を使っている場合、一貫性のある技術スタックになって嬉しい。また TypeScript であれば型安全にメールがかけるので、なお嬉しい。
基本形
基本的には、下記のようにすれば、静的な HTML が吐き出せる。これを Mailgun などに送れば OK。
Props を使って、型安全にメールのテンプレートに変数を渡すことができる。
import React from "react"
import ReactDOMServer from "react-dom/server"
const Email = ({ userName }: { userName: string }) => (
<html>
<body>
<div>
<p>Hello, {userName}!</p>
</div>
</body>
</html>
)
const html = ReactDOMServer.renderToStaticMarkup(<Email userName="Kazuya" />)
CSS を入れる
CSS は <head />
の中に書いてしまうのが一番簡単。また Juice を使ってインライン化すれば、 Outlook などにも対応できる。
import juice from "juice"
import React from "react"
import ReactDOMServer from "react-dom/server"
const Email = () => (
<html>
<head>
<style>{`
.button {
padding: 4px;
background-color: #333;
color: #fff;
}
`}</style>
</head>
<body>
<div>
<p>Hello, email!</p>
<button className="button">LEARN MORE</button>
</div>
</body>
</html>
)
const staticMarkup = ReactDOMServer.renderToStaticMarkup(
<Email userName="Kazuya" />,
)
const html = juice(staticMarkup)
Context を利用する
このままでも良いのだが、 React らしく Context API を使うと更に便利。使いどころとしては、定型的な文章、例えば送信するユーザー情報や Unsubscribe のリンクを孫コンポーネント等から利用できるようにする。
import juice from "juice"
import React from "react"
import ReactDOMServer from "react-dom/server"
const EmailContext = React.createContext()
const Layout: React.FC = ({ children }) => {
const { user } = React.useContext(EmailContext)
return (
<html>
<body>
<div>
<div>Hey ${user.name},</div>
<div>{children}</div>
</div>
<a href={`https://myapp.com/unsubscribe?token=${user.token}`}>
Unsubscribe
</a>
</body>
</html>
)
}
const Email = () => (
<Layout>
<div>Hello from Email</div>
</Layout>
)
const staticMarkup = ReactDOMServer.renderToStaticMarkup(
<EmailContext.Provider value={{ name: "Kazuya", token: "123456abc" }}>
<Email />
</EmailContext.Provider>,
)
const html = juice(staticMarkup)
おまけ: TailwindCSS を入れる
TailwindCSS をフロントエンドで使っている場合、同じ要領で書けると更に嬉しい。単純に <style>
内に TailwindCSS を全て入れても良いのだが、せっかくなので tailwind-rn
というライブラリを使ってみた。
もともとは React Native 用のライブラリだが、やっていることは TailwindCSS のクラスを React が扱える style
のオブジェクトに変換しているだけなので、いい感じに使える。普通に React Native で TailwindCSS 使いたい時にもお薦め。
import React from "react"
import ReactDOMServer from "react-dom/server"
import tailwind from "tailwind-rn"
const Email = () => (
<html>
<body>
<div>
<p>Hello, email!</p>
<button style={tailwind("p-1 bg-gray-800 text-white")}>
LEARN MORE
</button>
</div>
</body>
</html>
)
const html = ReactDOMServer.renderToStaticMarkup(element)
tailwind-rn
はインラインで CSS を記述するようになるので、 TailwindCSS で完結させれば Juice が必要なくなったのも嬉しい。
注意点としては text-lg
等のスタイルを利用すると、 line-height
がおかしなことになる。結局 Juice
でグローバルに * { line-height: 1.6 !important }
というスタイルを当てているが、もっと良い解決策がありそう。単位を必ず px
にするとか。
Discussion