There is one more improvement we can do to our authentication system, and that is the magic link email we send to the user. By default, it looks like this:
Sending custom email using Resend
Again, we are going to use Resend for this job. First, you need to install the resend
package, which allows us to send emails programmatically.
1npm install resend
However, there is one small problem here. Because the Resend provider from Auth.js and the resend
package we just installed have the same name. To avoid conflict in the program, we'll import the Resend
package as ResendClient
.
libs/auth.js
1. . .
2
3// The Resend provider from Auth.js, used to integrate Auth.js with Resend
4import Resend from "next-auth/providers/resend";
5
6// The Resend client from Resend, used to send emails
7import { Resend as ResendClient } from "resend";
8
9. . .
And then initialize the Resend client with the same API key we used before:
1. . .
2
3// The Resend provider from Auth.js, used to integrate Auth.js with Resend
4import Resend from "next-auth/providers/resend";
5
6// The Resend client from Resend, used to send emails
7import { Resend as ResendClient } from "resend";
8
9const resend = new ResendClient(process.env.AUTH_RESEND_KEY);
10
11. . .
Then in the Auth.js configurations where we configure the Resend provider, specify a sendVerificationRequest
option like this:
1. . .
2
3export const { handlers, signIn, signOut, auth } = NextAuth({
4 adapter: PrismaAdapter(prisma),
5 providers: [
6 Resend({
7 sendVerificationRequest: async ({ identifier: email, url }) => {
8 await resend.emails.send({
9 from: "no-reply@send.thedevspace.io",
10 to: email,
11 subject: "Sign in to Your App",
12 html: `<a href="${url}">Sign in</a>`,
13 });
14 },
15 }),
16 ],
17 pages: {
18 verifyRequest: "/check-your-email",
19 },
20});
By default, when the user tries to sign in (send a verification request), a default verification email will be sent, but the sendVerificationRequest
option allows you to customize this behavior.
This option accepts a callback function with two input arguments, identifier
and url
. The identifier
is the user email address, which is where the verification email should be delivered to, and the url
is the magic link that will be embedded into the email content.
1async ({ identifier: email, url }) => {
2 await resend.emails.send({
3 from: "no-reply@send.thedevspace.io",
4 to: email,
5 subject: "Sign in to Your App",
6 html: `<a href="${url}">Sign in</a>`,
7 });
8},
Inside the callback function, we'll send the email using the send()
method provided by the Resend client.
The send()
method requires you to provide the from
, to
, subject
and html
.
html
defines the email content, which as you can imagine, is a piece of HTML code. To keep things simpler, we’ve stripped away all unnecessary content and only kept the magic link.
Save the changes and try to sign in again, and you should receive the following email:
Using a email template
We were able to customize the email, but as you can see, this method requires us to write raw HTML and CSS code, which can be a lot of trouble, especially when there's no proper syntax highlighting.
Instead, we can use a email template. Resend supports React Email, which allows us to create emails just like creating React components.
As an example, create a email-template.jsx
under components
:
1src
2├── app
3│ ├── api
4│ ├── check-your-email
5│ ├── favicon.ico
6│ ├── globals.css
7│ ├── layout.jsx
8│ ├── page.jsx
9│ └── signin
10│ └── page.jsx
11├── components
12│ ├── email-template.jsx <===== Here is your email template
13│ ├── footer.jsx
14│ └── navbar.jsx
15└── libs
The best part about using React Email is that you can use TailwindCSS inside the email template. However, since most email clients do not support CSS frameworks, we need to do things a bit differently.
Install the @react-email/components
package with the following command.
1npm install @react-email/components
Import the Tailwind component and make sure your entire email template is wrapped inside. This component enables you to use TailwindCSS directly.
components/email-template.jsx
1import { Tailwind } from "@react-email/components";
2
3export function EmailTemplate({ url }) {
4 return (
5 <Tailwind>
6 <div className="bg-gray-50 p-5 font-sans text-gray-800">
7 <div className="max-w-[600px] mx-auto bg-white rounded-lg shadow-lg p-6 text-center">
8 <h1 className="text-2xl font-bold mb-4 text-gray-800">
9 Welcome to Your App
10 </h1>
11 <p className="text-base leading-relaxed mb-6 text-gray-600">
12 Thank you for signing up! Please verify your email address by
13 clicking the button below:
14 </p>
15 <a
16 href={url}
17 className="inline-block bg-blue-600 text-white text-base font-bold py-3 px-6 rounded-lg no-underline mb-6">
18 Verify Email
19 </a>
20 <p className="text-sm text-gray-600 mt-4">
21 If you didn't request this email, you can safely ignore it.
22 </p>
23 </div>
24 </div>
25 </Tailwind>
26 );
27}
Lastly, go back to Auth.js configurations and replace html
with react
like this:
libs/auth.js
1. . .
2import { EmailTemplate } from "@/components/email-template";
3
4const resend = new ResendClient(process.env.AUTH_RESEND_KEY);
5
6export const { handlers, signIn, signOut, auth } = NextAuth({
7 adapter: PrismaAdapter(prisma),
8 providers: [
9 Resend({
10 sendVerificationRequest: async ({ identifier: email, url }) => {
11 await resend.emails.send({
12 from: "no-reply@send.thedevspace.io",
13 to: email,
14 subject: "Sign in to Your App",
15 react: EmailTemplate({ url: url }),
16 });
17 },
18 }),
19 ],
20 pages: {
21 verifyRequest: "/check-your-email",
22 },
23});
Don't forget to pass the magic link (url
) to the template.
And now, try to sign in again, and you should get this email in our inbox.