mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 21:59:16 +03:00
Closes DOCS-1260 _See WAVE plugin no longer flags a jump in header hierarchy. Preview on left._ <img width="708" height="699" alt="Screenshot 2026-07-29 at 2 30 11 PM" src="https://github.com/user-attachments/assets/be5bf335-9e03-474f-8215-06fa505ab2db" /> ## Problem Beyond the 4 shared components fixed in [#48456](https://github.com/supabase/supabase/pull/48456), the [header hierarchy report](https://app.notion.com/p/supabase/Playwright-E2E-Triage-Reports-3ab5004b775f81e3bc60d058fa5a02c1) found plain content headings that skip a level. For example, you may see a `##` followed directly by an `####`. Jumps in headers breaks page navigation for screen reader users, who jump between headings expecting each level to nest one at a time. ## Solution Adjusted heading levels across the affected guide and troubleshooting pages so every section nests correctly, with no skipped levels. **Staging previews:** | Page | Preview | | --- | --- | | `/guides/api/rest/postgrest-error-codes` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/api/rest/postgrest-error-codes) | | `/guides/auth/oauth-server/getting-started` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/auth/oauth-server/getting-started) | | `/guides/database/custom-postgres-config` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/custom-postgres-config) | | `/guides/database/drizzle` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/drizzle) | | `/guides/database/extensions` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/extensions) | | `/guides/database/extensions/pgaudit` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/extensions/pgaudit) | | `/guides/database/postgres-js` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/postgres-js) | | `/guides/database/replication/manual-replication-monitoring` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/replication/manual-replication-monitoring) | | `/guides/database/replication/manual-replication-setup` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/replication/manual-replication-setup) | | `/guides/database/replication/pipelines-monitoring` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/database/replication/pipelines-monitoring) | | `/guides/functions/debugging-tools` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/debugging-tools) | | `/guides/functions/development-tips` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/development-tips) | | `/guides/functions/examples/auth-send-email-hook-react-email-resend` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/examples/auth-send-email-hook-react-email-resend) | | `/guides/functions/examples/image-manipulation` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/examples/image-manipulation) | | `/guides/functions/examples/semantic-search` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/examples/semantic-search) | | `/guides/functions/examples/send-emails` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/examples/send-emails) | | `/guides/functions/examples/sentry-monitoring` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/examples/sentry-monitoring) | | `/guides/functions/wasm` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/functions/wasm) | | `/guides/getting-started` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/getting-started) | | `/guides/platform/aws-marketplace` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/platform/aws-marketplace) | | `/guides/platform/aws-marketplace/faq` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/platform/aws-marketplace/faq) | | `/guides/platform/billing-faq` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/platform/billing-faq) | | `/guides/platform/ipv4-address` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/platform/ipv4-address) | | `/guides/platform/migrating-within-supabase/backup-restore` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/platform/migrating-within-supabase/backup-restore) | | `/guides/platform/privatelink` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/platform/privatelink) | | `/guides/queues/api` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/queues/api) | | `/guides/resources` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/resources) | | `/guides/security/hipaa-compliance` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/security/hipaa-compliance) | | `/guides/security/security-testing` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/security/security-testing) | | `/guides/security/soc-2-compliance` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/security/soc-2-compliance) | | `/guides/self-hosting` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/self-hosting) | | `/guides/storage/cdn/fundamentals` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/storage/cdn/fundamentals) | | `/guides/storage/debugging/logs` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/storage/debugging/logs) | | `/guides/storage/production/scaling` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/storage/production/scaling) | | `/guides/storage/schema/helper-functions` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/storage/schema/helper-functions) | | `/guides/storage/serving/image-transformations` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/storage/serving/image-transformations) | | `/guides/storage/uploads/resumable-uploads` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/storage/uploads/resumable-uploads) | | `/guides/troubleshooting/an-invalid-response-was-received-from-the-upstream-server-error-when-querying-auth-RI4Vl-` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/an-invalid-response-was-received-from-the-upstream-server-error-when-querying-auth-RI4Vl-) | | `/guides/troubleshooting/are-all-features-available-in-self-hosted-supabase-THPcqw` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/are-all-features-available-in-self-hosted-supabase-THPcqw) | | `/guides/troubleshooting/avoiding-timeouts-in-long-running-queries-6nmbdN` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/avoiding-timeouts-in-long-running-queries-6nmbdN) | | `/guides/troubleshooting/database-api-42501-errors` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/database-api-42501-errors) | | `/guides/troubleshooting/disabling-prepared-statements-qL8lEL` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/disabling-prepared-statements-qL8lEL) | | `/guides/troubleshooting/discovering-and-interpreting-api-errors-in-the-logs-7xREI9` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/discovering-and-interpreting-api-errors-in-the-logs-7xREI9) | | `/guides/troubleshooting/edge-function-504-error-response` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/edge-function-504-error-response) | | `/guides/troubleshooting/high-cpu-and-slow-queries-with-error-must-be-a-superuser-to-terminate-superuser-process` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/high-cpu-and-slow-queries-with-error-must-be-a-superuser-to-terminate-superuser-process) | | `/guides/troubleshooting/how-postgres-chooses-which-index-to-use-_JHrf4` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/how-postgres-chooses-which-index-to-use-_JHrf4) | | `/guides/troubleshooting/how-to-change-max-database-connections-_BQ8P5` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/how-to-change-max-database-connections-_BQ8P5) | | `/guides/troubleshooting/how-to-interpret-and-explore-the-postgres-logs-OuCIOj` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/how-to-interpret-and-explore-the-postgres-logs-OuCIOj) | | `/guides/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM) | | `/guides/troubleshooting/http-api-issues` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/http-api-issues) | | `/guides/troubleshooting/increase-vector-lookup-speeds-by-applying-an-hsnw-index-ohLHUM` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/increase-vector-lookup-speeds-by-applying-an-hsnw-index-ohLHUM) | | `/guides/troubleshooting/interpreting-supabase-grafana-cpu-charts-9JSlkC` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/interpreting-supabase-grafana-cpu-charts-9JSlkC) | | `/guides/troubleshooting/interpreting-supabase-grafana-io-charts-MUynDR` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/interpreting-supabase-grafana-io-charts-MUynDR) | | `/guides/troubleshooting/new-branch-doesnt-copy-database` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/new-branch-doesnt-copy-database) | | `/guides/troubleshooting/not-receiving-auth-emails-from-the-supabase-project-OFSNzw` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/not-receiving-auth-emails-from-the-supabase-project-OFSNzw) | | `/guides/troubleshooting/resolving-500-status-authentication-errors-7bU5U8` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/resolving-500-status-authentication-errors-7bU5U8) | | `/guides/troubleshooting/resolving-cannot-execute-update-in-a-read-only-transaction-on-transaction-pooler-connections-ef582c` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/resolving-cannot-execute-update-in-a-read-only-transaction-on-transaction-pooler-connections-ef582c) | | `/guides/troubleshooting/resolving-database-hostname-and-managing-your-ip-address-pVlwE0` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/resolving-database-hostname-and-managing-your-ip-address-pVlwE0) | | `/guides/troubleshooting/rls-simplified-BJTcS8` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/rls-simplified-BJTcS8) | | `/guides/troubleshooting/security-of-anonymous-sign-ins-iOrGCL` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/security-of-anonymous-sign-ins-iOrGCL) | | `/guides/troubleshooting/supabase--your-network-ipv4-and-ipv6-compatibility-cHe3BP` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/supabase--your-network-ipv4-and-ipv6-compatibility-cHe3BP) | | `/guides/troubleshooting/supabase-grafana-memory-charts` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/supabase-grafana-memory-charts) | | `/guides/troubleshooting/supavisor-faq-YyP5tI` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/supavisor-faq-YyP5tI) | | `/guides/troubleshooting/tracking-postgres-role-activity-to-specific-dashboard-users-8d3715` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/tracking-postgres-role-activity-to-specific-dashboard-users-8d3715) | | `/guides/troubleshooting/transferring-from-cloud-to-self-host-in-supabase-2oWNvW` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/transferring-from-cloud-to-self-host-in-supabase-2oWNvW) | | `/guides/troubleshooting/understanding-postgresql-explain-output-Un9dqX` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/understanding-postgresql-explain-output-Un9dqX) | | `/guides/troubleshooting/understanding-postgresql-logging-levels-and-how-they-impact-your-project-KXiJRm` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/understanding-postgresql-logging-levels-and-how-they-impact-your-project-KXiJRm) | | `/guides/troubleshooting/vercel-integration-environment-variables-not-syncing-for-persistent-git-branches-b9191e` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/vercel-integration-environment-variables-not-syncing-for-persistent-git-branches-b9191e) | | `/guides/troubleshooting/why-are-there-gaps-in-my-postgres-id-sequence-Frifus` | [Preview](https://docs-git-docs-heading-hierarchy-supabase.vercel.app/docs/guides/troubleshooting/why-are-there-gaps-in-my-postgres-id-sequence-Frifus) | ## Manual testing 1. See affected pages. Recommend running a browser plugin like WAVE and selecting the **Structure** tab. 2. See the headings do not skip. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Documentation** * Standardized heading hierarchy across many guides and troubleshooting articles to improve readability and navigation. * Updated several documentation link targets to the correct new locations. * Reformatted multiple sections (including replication monitoring, Edge Functions, Storage, authentication, security, and networking) without changing instructions. * Queue Data API docs were restructured via heading-level adjustments (no operational changes). * Billing FAQ received clearer, more detailed payment-failure and tax guidance, plus related link updates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
299 lines
7.9 KiB
Plaintext
299 lines
7.9 KiB
Plaintext
---
|
||
title: 'Custom Auth Emails with React Email and Resend'
|
||
description: 'Use the send email hook to send custom auth emails with React Email and Resend in Supabase Edge Functions.'
|
||
tocVideo: 'tlA7BomSCgU'
|
||
---
|
||
|
||
Use the [send email hook](/docs/guides/auth/auth-hooks/send-email-hook?queryGroups=language&language=http) to send custom auth emails with [React Email](https://react.email/) and [Resend](https://resend.com/) in Supabase Edge Functions.
|
||
|
||
<Admonition type="note">
|
||
|
||
Prefer to jump straight to the code? [Check out the example on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/auth-hook-react-email-resend).
|
||
|
||
</Admonition>
|
||
|
||
## Prerequisites
|
||
|
||
To get the most out of this guide, you’ll need to:
|
||
|
||
- [Create a Resend API key](https://resend.com/api-keys)
|
||
- [Verify your domain](https://resend.com/domains)
|
||
|
||
Make sure you have the latest version of the [Supabase CLI](/docs/guides/local-development/cli/getting-started#installing-the-supabase-cli) installed.
|
||
|
||
## 1. Create Supabase function
|
||
|
||
Create a new function locally:
|
||
|
||
```bash
|
||
supabase functions new send-email
|
||
```
|
||
|
||
## 2. Edit the handler function
|
||
|
||
Paste the following code into the `index.ts` file:
|
||
|
||
```tsx supabase/functions/send-email/index.ts
|
||
import { renderAsync } from 'npm:@react-email/components@^1'
|
||
import { withSupabase } from 'npm:@supabase/server@^1'
|
||
import React from 'npm:react@^19'
|
||
import { Resend } from 'npm:resend@^6'
|
||
import { Webhook } from 'npm:standardwebhooks@^1'
|
||
|
||
import { MagicLinkEmail } from './_templates/magic-link.tsx'
|
||
|
||
const resend = new Resend(Deno.env.get('RESEND_API_KEY') as string)
|
||
const hookSecret = (Deno.env.get('SEND_EMAIL_HOOK_SECRET') as string).replace('v1,whsec_', '')
|
||
|
||
export default {
|
||
fetch: withSupabase({ auth: 'none' }, async (req) => {
|
||
if (req.method !== 'POST') {
|
||
return Response.json({ error: 'not allowed' }, { status: 400 })
|
||
}
|
||
|
||
const payload = await req.text()
|
||
const headers = Object.fromEntries(req.headers)
|
||
const wh = new Webhook(hookSecret)
|
||
try {
|
||
const {
|
||
user,
|
||
email_data: { token, token_hash, redirect_to, email_action_type },
|
||
} = wh.verify(payload, headers) as {
|
||
user: {
|
||
email: string
|
||
}
|
||
email_data: {
|
||
token: string
|
||
token_hash: string
|
||
redirect_to: string
|
||
email_action_type: string
|
||
site_url: string
|
||
token_new: string
|
||
token_hash_new: string
|
||
}
|
||
}
|
||
|
||
const html = await renderAsync(
|
||
React.createElement(MagicLinkEmail, {
|
||
supabase_url: Deno.env.get('SUPABASE_URL') ?? '',
|
||
token,
|
||
token_hash,
|
||
redirect_to,
|
||
email_action_type,
|
||
})
|
||
)
|
||
|
||
const { error } = await resend.emails.send({
|
||
from: 'welcome <onboarding@resend.dev>',
|
||
to: [user.email],
|
||
subject: 'Supa Custom MagicLink!',
|
||
html,
|
||
})
|
||
if (error) {
|
||
throw error
|
||
}
|
||
} catch (error) {
|
||
console.log(error)
|
||
return Response.json(
|
||
{
|
||
error: {
|
||
http_code: error.code,
|
||
message: error.message,
|
||
},
|
||
},
|
||
{ status: 401 }
|
||
)
|
||
}
|
||
|
||
return Response.json({})
|
||
}),
|
||
}
|
||
```
|
||
|
||
## 3. Create React Email templates
|
||
|
||
Create a new `_templates` folder following the [recommended project structure](/docs/guides/functions/development-environment#recommended-project-structure) and add a new `magic-link.tsx` file with the following code:
|
||
|
||
```tsx supabase/functions/send-email/_templates/magic-link.tsx
|
||
import {
|
||
Body,
|
||
Container,
|
||
Head,
|
||
Heading,
|
||
Html,
|
||
Link,
|
||
Preview,
|
||
Text,
|
||
} from 'npm:@react-email/components@^1'
|
||
import * as React from 'npm:react@^19'
|
||
|
||
interface MagicLinkEmailProps {
|
||
supabase_url: string
|
||
email_action_type: string
|
||
redirect_to: string
|
||
token_hash: string
|
||
token: string
|
||
}
|
||
|
||
export const MagicLinkEmail = ({
|
||
token,
|
||
supabase_url,
|
||
email_action_type,
|
||
redirect_to,
|
||
token_hash,
|
||
}: MagicLinkEmailProps) => (
|
||
<Html>
|
||
<Head />
|
||
<Preview>Log in with this magic link</Preview>
|
||
<Body style={main}>
|
||
<Container style={container}>
|
||
<Heading style={h1}>Login</Heading>
|
||
<Link
|
||
href={`${supabase_url}/auth/v1/verify?token=${token_hash}&type=${email_action_type}&redirect_to=${redirect_to}`}
|
||
target="_blank"
|
||
style={{
|
||
...link,
|
||
display: 'block',
|
||
marginBottom: '16px',
|
||
}}
|
||
>
|
||
Click here to log in with this magic link
|
||
</Link>
|
||
<Text style={{ ...text, marginBottom: '14px' }}>
|
||
Or, copy and paste this temporary login code:
|
||
</Text>
|
||
<code style={code}>{token}</code>
|
||
<Text
|
||
style={{
|
||
...text,
|
||
color: '#ababab',
|
||
marginTop: '14px',
|
||
marginBottom: '16px',
|
||
}}
|
||
>
|
||
If you didn't try to login, you can safely ignore this email.
|
||
</Text>
|
||
<Text style={footer}>
|
||
<Link
|
||
href="https://demo.vercel.store/"
|
||
target="_blank"
|
||
style={{ ...link, color: '#898989' }}
|
||
>
|
||
ACME Corp
|
||
</Link>
|
||
, the famouse demo corp.
|
||
</Text>
|
||
</Container>
|
||
</Body>
|
||
</Html>
|
||
)
|
||
|
||
export default MagicLinkEmail
|
||
|
||
const main = {
|
||
backgroundColor: '#ffffff',
|
||
}
|
||
|
||
const container = {
|
||
paddingLeft: '12px',
|
||
paddingRight: '12px',
|
||
margin: '0 auto',
|
||
}
|
||
|
||
const h1 = {
|
||
color: '#333',
|
||
fontFamily:
|
||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||
fontSize: '24px',
|
||
fontWeight: 'bold',
|
||
margin: '40px 0',
|
||
padding: '0',
|
||
}
|
||
|
||
const link = {
|
||
color: '#2754C5',
|
||
fontFamily:
|
||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||
fontSize: '14px',
|
||
textDecoration: 'underline',
|
||
}
|
||
|
||
const text = {
|
||
color: '#333',
|
||
fontFamily:
|
||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||
fontSize: '14px',
|
||
margin: '24px 0',
|
||
}
|
||
|
||
const footer = {
|
||
color: '#898989',
|
||
fontFamily:
|
||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||
fontSize: '12px',
|
||
lineHeight: '22px',
|
||
marginTop: '12px',
|
||
marginBottom: '24px',
|
||
}
|
||
|
||
const code = {
|
||
display: 'inline-block',
|
||
padding: '16px 4.5%',
|
||
width: '90.5%',
|
||
backgroundColor: '#f4f4f4',
|
||
borderRadius: '5px',
|
||
border: '1px solid #eee',
|
||
color: '#333',
|
||
}
|
||
```
|
||
|
||
<Admonition type="note">
|
||
|
||
You can find a selection of React Email templates in the [React Email Examples](https://react.email/examples).
|
||
|
||
</Admonition>
|
||
|
||
## 4. Deploy the Function
|
||
|
||
Deploy function to Supabase:
|
||
|
||
```bash
|
||
supabase functions deploy send-email --no-verify-jwt
|
||
```
|
||
|
||
Note down the function URL, you will need it in the next step!
|
||
|
||
## 5. Configure the Send Email Hook
|
||
|
||
- Go to the [Auth Hooks](/dashboard/project/_/auth/hooks) section of the Supabase dashboard and create a new "Send Email hook".
|
||
- Select HTTPS as the hook type.
|
||
- Paste the function URL in the "URL" field.
|
||
- Click "Generate Secret" to generate your webhook secret and note it down.
|
||
- Click "Create" to save the hook configuration.
|
||
|
||
Store these secrets in your `.env` file.
|
||
|
||
```bash supabase/functions/.env
|
||
RESEND_API_KEY=your_resend_api_key
|
||
SEND_EMAIL_HOOK_SECRET="v1,whsec_<base64_secret>"
|
||
```
|
||
|
||
<Admonition type="note">
|
||
|
||
You can generate the secret in the [Auth Hooks](/dashboard/project/_/auth/hooks) section of the Supabase dashboard.
|
||
|
||
</Admonition>
|
||
|
||
Set the secrets from the `.env` file:
|
||
|
||
```bash
|
||
supabase secrets set --env-file supabase/functions/.env
|
||
```
|
||
|
||
Now your Supabase Edge Function will be triggered anytime an Auth Email needs to be sent to the user!
|
||
|
||
## More resources
|
||
|
||
- [Send Email Hooks](/docs/guides/auth/auth-hooks/send-email-hook)
|
||
- [Auth Hooks](/docs/guides/auth/auth-hooks)
|