
Create & Send Custom Emails with React Email & Resend
Create & Send Custom Emails with React Email & Resend
Build custom email templates using React and Typescript and send them using Resend.
We’ll walk through setting up React Email in a Next.js project, creating a new React email template, setting up an API route, and using Resend to send the emails.
🧰 Resources
Code: https://github.com/colbyfayock/my-rea…
Starter: https://github.com/colbyfayock/demo-e…
Tutorial: https://kdta.io/7nzEh
📺 YouTube: https://youtube.com/colbyfayock?sub_c…
🐦 Twitter: https://twitter.com/colbyfayock
📹 Twitch: https://twitch.tv/colbyfayock
✉️ Newsletter: https://www.colbyfayock.com/newsletter/
🎥 What I Use: https://www.colbyfayock.com/uses
#colbyfayock #reactjs #email #nextjs #resend #webdevelopment
Content
0.06 -> if you've ever handcrafted email
1.68 -> templates you know could be a little bit
3.3 -> challenging to get right but now using
5.16 -> tools like react email we can build all
7.259 -> those emails right inside of react and
9.42 -> have more confidence of the experience
10.679 -> we're delivering to people's inbox
13.519 -> we're going to dig into react email and
16.02 -> see how we can use it to easily craft
17.64 -> our custom templates to be able to send
19.859 -> our transactional emails to do this I
21.779 -> created a new nexjs application where I
24 -> created a simple signup form where I
25.439 -> have a name and email field that once
27.48 -> you click sign up we're just going to
28.859 -> send a transactional email as a way to
30.599 -> fake it just to see how this works if
32.34 -> the UI looks familiar I use Shad CN UI
34.8 -> in order to just simply build that form
36.48 -> up but inside of the code I'm using the
38.16 -> nexjs app directory where I have my
40.079 -> components where I have a client
41.879 -> component which includes that sign up
43.739 -> form that includes those different
45.6 -> labels the inputs as well as just some
47.82 -> simple submission handling where we're
49.86 -> going to be able to send that email once
51.3 -> we have it configured now to get started
52.8 -> with react email we can either use the
54.48 -> automatic setup which will create a new
56.039 -> project but I'm going to go ahead and
57.48 -> manually set this up as I want to create
59.1 -> it inside my existing project now this
61.02 -> also kind of depends on how you want to
62.52 -> organize your project I want to actually
64.08 -> organize Mine by including my emails in
66.479 -> the source directory just because that
67.92 -> is a little bit more sane in my brain so
69.72 -> I'm going to skip down and actually
70.799 -> start to install the dependencies so I'm
72.72 -> going to copy this line and install it
74.64 -> in my project and then we can see the
76.32 -> next step is to actually add a Dev
77.82 -> command inside of our project which is
79.619 -> email Dev so in my package.json I'm
82.619 -> going to specify my email which is email
86.28 -> Dev and then it gives me a really basic
88.68 -> email template that I can include so I'm
90.24 -> going to go ahead and copy that and
91.439 -> inside of my source directory I'm going
93 -> to create a new directory called emails
95.88 -> and inside I'm going to create my
100.04 -> welcome.tsx file and paste in that new
102.479 -> template and now we should be able to
103.86 -> get started running our Dev command but
105.24 -> we need to do one little thing if you're
106.68 -> organizing it the way I'm organizing it
108.42 -> so I'm going to head over to the CLI
109.979 -> where we have the option of passing in a
112.32 -> directory flag where I'm going to
114 -> specify the directory where I include my
115.56 -> emails so I have my emails inside of
117.54 -> source emails so I'm going to specify
119.759 -> directory and then Source emails but now
123.299 -> when I run npm run email and if this is
126.24 -> your first time running it it's going to
127.439 -> install send dependencies additional to
129.239 -> help set up the environment but once
130.739 -> it's ready we now have a Dev server
132.18 -> available where if I open that up inside
133.56 -> my browser where now we're in through
135.36 -> the preview mode where I can select my
137.04 -> email welcome on the side and I see that
139.14 -> new welcome email which because I just
141.18 -> copied that template all I have right
142.62 -> now is Click me which opens that link
144.54 -> but we can even start to see the source
146.4 -> that built that which is what we copy
147.9 -> and pasted from the react email site now
149.76 -> if we start to look at that template
150.9 -> that we built this is all react and
152.819 -> we're pulling in a lot of these
153.959 -> components from the react email Library
155.94 -> if we look over at the documentation we
157.92 -> can see that we have a lot of components
159.66 -> that are available to us in order to
161.099 -> build a lot of those blocks that are
162.959 -> traditionally inside of an email if
164.64 -> Tailwind is your thing there's even a
166.14 -> Tailwind component that gives you the
167.64 -> ability to wrap the email to expose
169.8 -> those classes to take advantage of them
171.9 -> inside of your email design but I'm not
173.459 -> going to get too far into going through
174.66 -> all these components what I'm going to
175.92 -> actually do is head back up and find the
178.98 -> examples where inside of this they
181.44 -> provide a bunch of different examples
183.36 -> from other templates that you might have
185.22 -> seen from some of your favorite Services
186.78 -> now the nice thing about this isn't that
188.64 -> you can just copy and paste somebody
189.9 -> else's template and ship it it's a good
192.18 -> way to get started with creating your
193.92 -> own template for instance this notion
195.659 -> magic link template is pretty simple and
197.7 -> for now that's all I want for my welcome
199.08 -> email I just want to welcome them
200.34 -> something simple so if I look inside the
202.379 -> source we can see that I'm pulling in
203.879 -> these components from react email
205.62 -> components so first of all we'll need to
207.72 -> install that top level package or
209.64 -> install all the different component
211.26 -> packages but first let's copy this
213.239 -> template and I'm going to paste that and
214.8 -> overwrite all of my welcome email
216.42 -> template but now let's go ahead and
217.86 -> install react email components now the
220.08 -> tricky thing right now is if you try to
221.519 -> actually command C or Ctrl C rather and
224.4 -> shut down this process you might run
226.319 -> into some issues so we'll actually have
227.879 -> to just close this tab for now I'm going
229.62 -> to go ahead and install my react email
231.659 -> components and if I run npm run email
234.54 -> again we can see that aside from the
236.519 -> static asset my email now looks like
238.62 -> that notion email now because this is
240.299 -> react I can really start to change this
241.98 -> to whatever I want such as how about I
243.9 -> just say welcome for the heading I don't
245.879 -> need this link so I'm going to get rid
247.26 -> of that instead of this text I'm going
248.819 -> to say thank you for signing up for the
251.819 -> best newsletter in the world rather
255.42 -> Galaxy and then I can get rid of some of
257.639 -> the other stuff since I don't
259.019 -> necessarily need it for my email but
261.479 -> once we have it to our liking we can see
263.22 -> all those changes inside of our Dev
264.6 -> environment we can even change the
266.04 -> Styles such as if I wanted to change
267.36 -> these footer Styles instead of being a
269.16 -> light gray maybe I just once you make it
271.32 -> purple or blue violet rather we can see
274.8 -> that it grabs those Styles and applies
276.419 -> them so we can have as much control over
278.34 -> this template it as we might have with
280.259 -> our traditional applications now because
282 -> we're in react you can really
283.139 -> componentize this and set this up
284.699 -> exactly how you want to make sure you
286.44 -> have all your reusable components for
288.36 -> building your emails but once we
289.979 -> ultimately have our template including
291.84 -> any renaming or any kind of cleanup that
294 -> you need to do welcome to this
295.62 -> newsletter we can start to think about
297.36 -> how we're actually going to send this
298.919 -> email now react email comes with a bunch
300.84 -> of different Integrations that we can
302.16 -> use but resend is actually the creators
303.96 -> of react email and it's pretty nice and
305.88 -> simple to use so let's try that out if
307.86 -> you haven't heard of resend before it's
309.18 -> an easy to use API for sending emails
311.46 -> looking at one of its examples that
313.139 -> doesn't use react email we can see that
315.12 -> once you set it up we really just have
316.74 -> this one send method which you specify
318.9 -> you're from and two and subject which of
320.82 -> course you need but then you have your
322.02 -> HTML that you can send you can even send
324 -> your text-based emails which is
325.5 -> important when you're sending it for
326.759 -> people who don't accept HTML emails but
329.34 -> of course we also have the ability to
330.9 -> send those react email templates and
332.759 -> once we import it we can pass that in as
334.38 -> the react option which it's going to
336.18 -> build it we can even pass in our Dynamic
337.979 -> parameters to build that template and
339.9 -> then we can have that sent out along
341.46 -> with everything else so let's get
342.9 -> started with that let's first install
344.1 -> resend so I'm going to paste that in my
346.02 -> terminal we can Skip Along through
347.46 -> creating our email template because we
348.9 -> already have that but then we ultimately
350.46 -> want to be able to send that email so
352.259 -> we're going to first import resend into
354.36 -> a new location where because we're
356.28 -> working inside an xjs we have a few
357.96 -> options such as maybe we want to use the
359.699 -> new server actions which if you want to
361.139 -> check out a video of how to work with
362.34 -> those you can check out this link above
364.259 -> but how about we just use a traditional
366 -> API route we're inside of app directory
368.22 -> let's create a new folder called API
370.62 -> which is going to include our different
372.06 -> apis I'm going to then include a new
375 -> directory called email and then inside
377.4 -> of that let's create a new route.ts
380.16 -> we're going to start off by exporting a
382.56 -> new async function called post we're
385.62 -> inside it's going to be where we're
387.12 -> going to perform that email send so at
389.039 -> the top of this I'm going to import my
390.72 -> resend from resend I'm then going to
393 -> want to configure my resend account and
394.8 -> paste that in right at the top now of
396.419 -> course we need to replace this with our
397.979 -> resend API key or if you don't yet have
399.96 -> your resend account you can head over to
401.639 -> get started where you can either create
403.5 -> using an email or signing up with GitHub
405.479 -> but once you're signed in we can head
406.8 -> over to the API Keys section where here
409.5 -> we have the ability to create and manage
410.88 -> our keys so if I click create API key
412.68 -> I'm going to be able to call this my
414.3 -> react email or whatever you want to make
416.28 -> sure that you understand what that is
417.84 -> you can set the different permissions
419.4 -> where for now it's really just sending
421.08 -> or flow access so I'm just going to
422.639 -> leave it as is we can even select all
424.259 -> the different domains we want which you
425.819 -> might not have configured if this is
427.199 -> your first time actually creating an
428.639 -> account but I'm going to go ahead and
429.96 -> click add where now we can see that it
431.699 -> generated my new API key and some things
433.8 -> to keep in mind is you're only going to
435.18 -> see this key once like they say but make
437.1 -> sure you do store this safely you don't
438.479 -> want to put this inside of your code you
440.099 -> don't want to expose this and make it
441.72 -> publicly available so you can avoid
443.639 -> people actually abusing your API key
445.319 -> meaning instead of just going ahead and
447.24 -> pasting it right here what we're going
448.62 -> to do is instead create an environment
450 -> variable so inside of the root of our
452.16 -> project we're going to
453.62 -> create.env.local and then start and
455.819 -> inside I'm going to create my resend API
458.34 -> key and paste in that key we're inside
460.68 -> of my configuration I'm going to now
462.12 -> replace that with process.env with my
464.699 -> resend API key now back to the react
466.74 -> email guide we can see that we now have
468.66 -> this resend send email example so I'm
471 -> going to go ahead and copy this over to
472.62 -> my project paste it right inside of this
474.78 -> post but we do have a few things that we
476.58 -> need to update here first of all we're
477.84 -> going to actually pass in our template
479.28 -> as a function and first of all we need
481.08 -> to actually import that so I'm going to
482.88 -> import my welcome email from I'm going
486.84 -> to get my source and that's inside of
489.9 -> emails welcome I'm going to take that
491.94 -> welcome email and I'm just going to pass
493.5 -> that in as a function invocation but I'm
495.72 -> going to also update the 2 to my email
497.88 -> hello at colbyfayock.com we need to make
501.3 -> sure that we're actually going to await
502.86 -> this send email so that we can make sure
504.479 -> that it does actually send but then we
506.34 -> want to also make sure that we're
507.599 -> actually responding to this endpoint so
509.699 -> to do that we're going to import next
511.56 -> response
513.36 -> from next server and then we're going to
516.06 -> return
516.979 -> nextresponse dot Json and we're
519.539 -> eventually going to pass through our
520.919 -> Json response from this email or if we
523.02 -> prefer such as the receipt ID of that
525.66 -> email but for now let's just pass in an
527.58 -> empty object maybe we can just say
529.32 -> status okay but we also need a valid
532.68 -> from email address in order to properly
534.72 -> send this out now as a sneaky way just
536.58 -> to test this out if we actually go to
537.899 -> the resend docs they have examples that
540.18 -> include their resend.dev email address
542.16 -> which you don't want to try to use this
544.14 -> in a production basis that's not going
545.7 -> to end up well for you but since we're
547.26 -> just trying to test this out quick
548.339 -> before we set up our email let's just
550.32 -> copy one of these resend emails and once
552.42 -> I plug that in the only other thing is
554.22 -> that we currently have this setup as a
555.779 -> post request because we're going to be
557.1 -> posting Dynamic data so we can't really
559.14 -> test this out easily but if I'm going to
560.58 -> just template temporarily change this to
562.32 -> a get and if I actually hit that
563.88 -> endpoint inside of my browser we should
565.86 -> quickly see that email gets sent right
567.779 -> into our inbox now again you do not want
570.12 -> to use resend.dev as your email address
572.339 -> for sending these emails that's probably
574.26 -> going to get shut down really quickly
575.459 -> and this is really just a test that it
576.959 -> works but if you head over to your
578.459 -> resend account and go over to domains
580.2 -> they make it really easy to walk through
581.94 -> the process of actually adding a new
583.56 -> domain for your recent account but I
585.779 -> already have my mail.space jelly.dev
587.94 -> setup so I'm going to go ahead and use
589.14 -> that where then I can really send it
590.7 -> from whatever email address I want so
592.8 -> email at mail.space jelly.dev and again
595.38 -> let's hit that endpoint just to test
596.82 -> that out and we can now see that that
598.38 -> came through as Myspace jelly.dev
600.3 -> address but let's actually get this
601.56 -> working dynamically because I want to
602.94 -> send this to my specific customers I
604.62 -> don't want to just send a really General
606 -> welcome email to every single person I
608.22 -> want to personalize it such as I want to
610.2 -> pass in an object I want to say my first
612.42 -> name is Colby or whatever it is so let's
616.5 -> switch this back to a post request where
618.54 -> we're going to ultimately post that
619.56 -> information but inside of my sign up
621.3 -> form component I already have my handle
623.459 -> on submit set up where I'm going to kick
625.56 -> it into a loading State and I just had
627.18 -> this set time out here just to kind of
628.8 -> simulate somebody clicking that button
630.36 -> but what I'm going to want to do is I'm
632.04 -> going to await I'm going to a fetch
634.44 -> where I'm going to hit that API email
637.56 -> just like we did in the browser but I'm
639.42 -> going to set up a method of post and I'm
642.36 -> going to set up my body I'm going to say
644.779 -> json.stringa ringify or I'm going to
648 -> pass in a first name and let's say Cosmo
652.019 -> now because I'm actually making that
653.279 -> fetch request I'm going to go ahead and
654.54 -> get rid of this set timeout and just
656.04 -> make it ready again all this is really
657.72 -> doing right now is disabling the button
659.519 -> if it's currently loading but to
661.019 -> actually make this Dynamic I want to get
662.7 -> that Json body from this request so what
665.399 -> I can do is pass in a first argument of
667.8 -> request to this post request where to
671.64 -> make typescript happy we can set that as
673.62 -> an instance of request but all I'm going
676.079 -> to Simply do is set my data to await
678.839 -> request dot Json and I can even go
681.839 -> further and I can destructure that first
684.3 -> name from that Json I'm going to pass
687.06 -> that right into that welcome email now
689.04 -> as we can see typescript still isn't
690.6 -> happy because we don't technically have
691.92 -> that prop set up so just like any other
693.54 -> the react component I can now set up my
695.7 -> first name prop inside of this component
697.92 -> then we can set it up so that as soon as
700.5 -> we have that it's going to welcome that
702.839 -> first name now again to make typescript
704.579 -> happy we can set up our welcome email
707.7 -> props
709.62 -> where I'm going to set that up as my
712.44 -> interface and then my first name is a
715.2 -> string now if you notice the red
716.279 -> squiggly on the components I think it's
717.779 -> just a matter of the cached types or
719.94 -> whatever we can see that of course that
721.92 -> it's working completely fine now that we
724.079 -> actually have our signup form wired up
725.519 -> let's pass in our information let's add
727.62 -> the name of Cosmo which of course I
730.14 -> specified my first name but I'm just
731.459 -> going to put the first one in there I'm
732.779 -> going to put my email of hello at
734.959 -> colbyfayock.com but now I'm going to
737.04 -> click sign up and we can see that it's
738.66 -> currently loading but we should now see
740.279 -> that email pop into our inbox and we
742.079 -> even have that Dynamic value for the
744 -> name but we're still hard coding this
745.5 -> value so we can make sure that we
746.94 -> actually grab the value from our inputs
749.16 -> in addition to the email address to make
751.5 -> sure that that's Dynamic and pass that
753.12 -> through now to start I want to make sure
754.5 -> that I have my name of name and my name
757.44 -> of email and I went ahead and pasted
759.6 -> some of this code in as I'm not going to
760.92 -> try to type all this out with all the
762.779 -> type safety inside of this project as
764.94 -> it's a bit simpler without the types but
766.74 -> I'm going to set this up so that's an
768.36 -> HTML form element and on that includes
770.579 -> the elements property of the current
772.74 -> Target which is allows me to get all the
774.6 -> inputs from that so I'm going to make
776.1 -> sure that I have all the named inputs
778.2 -> I'm going to then collect all that data
780 -> onto the form data object which I can
782.339 -> then reference for my information
784.04 -> including passing in myform data.first
787.26 -> name as well as my email of
790.16 -> formdata.email now if you want to grab
792.06 -> that code you can find it in the
793.38 -> description inside of this video but now
795.48 -> that I'm sending that information I want
796.86 -> to make sure that I pass that first name
798.779 -> directly through but now that I'm also
800.88 -> sending in that email I want to also
802.68 -> pass through my email to the to property
805.5 -> and just to test that this is working
806.88 -> differently I'm going to update my email
808.86 -> address to a different email address and
810.779 -> Cosmo the space jellyfish I'm going to
813.3 -> go ahead and click sign up where I
814.44 -> actually don't think I have Hello setup
815.7 -> I'm going to change that to Cosmo at
817.339 -> spacejelly.dev and click sign up again
819.24 -> you can see that email pop in that
821.1 -> includes that email of Cosmo
823.519 -> spacejelly.dev as well as the cosmo the
825.839 -> space jellyfish but as we were able to
827.579 -> see this gave us a really easy way to
829.079 -> set up our email including setting up
831.06 -> the template itself with react and then
833.16 -> easily dynamically constructing and
834.66 -> sending that from an API route which
836.339 -> gives us a powerful way where at least a
838.26 -> simple way for being able to send those
839.519 -> transactional emails based off of our
841.68 -> application actions dealing with email
844.019 -> is a necessary evil as it's a powerful
846.36 -> way to deliver information to our
847.98 -> customers but it's also really tricky to
849.779 -> get right what some of your email horror
851.639 -> stories let me know in the comments if
853.139 -> you wanted to go deeper into some of the
854.579 -> new next.js app router capabilities such
857.04 -> as those server actions that I talked
858.48 -> about check my video where we perform
860.22 -> image search using those search actions
862.26 -> if you like this video make sure you hit
863.82 -> thumbs up subscribe and click that
865.32 -> little notification Bell for future web
866.88 -> dev content thanks for watching
Source: https://www.youtube.com/watch?v=D4pS4b9-DgA