Kembali ke panduan Deploy

Untuk pemula — tinggal ikuti & klik Salin

Integrasi email & autentikasi Gmail (gratis)

Mau website kamu bisa kirim email verifikasi, OTP, atau reset password? Ada dua jalur gratis yang sama-sama berfungsi sempurna: Resend (paling mudah, deliverability bagus) atau Gmail SMTPpakai App Password. Pilih salah satu — setiap nilai & kode ada tombol Salin.

Resend

3.000 email/bulan gratis, setup 5 menit.

Gmail SMTP

Tanpa daftar layanan, cukup App Password.

Aman

Pakai App Password & env, bukan password asli.

1
Pahami dulu

Resend vs Gmail SMTP — pilih yang mana?

Keduanya gratis dan bisa dipakai dari website Next.js kamu. Bedanya cuma soal kemudahan dan batas pemakaian:

HalResendGmail SMTP
BiayaGratis 3.000/bulanGratis (kuota Gmail)
Batas harian100/hari (free)±500/hari
SetupDaftar + 1 API keyAktifkan App Password
Masuk inboxSangat baikBaik untuk volume kecil
Cocok untukProduksi & skalaProyek pribadi/uji coba
Saran: Resend untuk aplikasi serius (lebih jarang masuk spam). Gmail SMTP kalau kamu cuma butuh kirim sedikit email dan tidak mau daftar layanan baru. Kode aplikasinya nyaris sama — kamu cuma ganti isi lib/email.ts.
2

Yang perlu disiapkan

Sebuah project Next.js (App Router) dan salah satu dari ini, sesuai jalur yang kamu pilih:

Jalur A — akun Resend

Daftar gratis di resend.com untuk dapat API key.

Jalur B — akun Gmail + 2FA

Verifikasi 2 langkah aktif agar bisa bikin App Password.

3
Pilih A atau B

Jalur A — Resend (paling mudah)

Buka resend.com/signup, daftar gratis, lalu masuk ke menu API Keys → Create API Key. Salin kuncinya (diawali re_) dan simpan baik-baik.

Pasang paket Resend di project kamu:

Terminal
# pakai pnpm
pnpm add resend

# atau npm
npm install resend
Untuk uji coba, kamu bisa langsung kirim dari alamat . Untuk produksi, tambahkan & verifikasi domain sendiri di menu Domains supaya email tidak masuk spam dan bisa pakai alamat @domainkamu.com.

Sudah punya API key? Lewati Langkah 4 & 5 dan lompat ke Langkah 6 untuk menyimpan env.

4

Jalur B — Gmail SMTP gratis

Tidak perlu daftar layanan apa pun. Kamu hanya akan mengaktifkan Verifikasi 2 Langkah lalu membuat App Password 16 karakter khusus untuk aplikasi kamu. Server SMTP Gmail yang dipakai:

Host
Port (SSL)
Port (TLS)
Jangan pakai password Gmail biasa di kode — Google memblokirnya. Wajib pakai App Password (Langkah 5).
5
Khusus Jalur B

Buat App Password Gmail

App Password hanya muncul jika Verifikasi 2 Langkah sudah aktif. Ikuti urutan ini:

  1. 1Buka myaccount.google.com/security lalu aktifkan 2-Step Verification.
  2. 2Setelah aktif, buka myaccount.google.com/apppasswords.
  3. 3Beri nama, misal , lalu klik Create.
  4. 4Google menampilkan 16 huruf (contoh abcd efgh ijkl mnop). Salin tanpa spasi jadi abcdefghijklmnop.

Lalu pasang paket pengirim email (nodemailer):

Terminal
# pakai pnpm
pnpm add nodemailer
pnpm add -D @types/nodemailer

# atau npm
npm install nodemailer
npm install --save-dev @types/nodemailer
App Password ini rahasia seperti password. Kalau bocor, tinggal hapus dari halaman App Passwords dan buat yang baru.
6

Simpan environment variables

Buat file .env.local di root project. Pakai blok sesuai jalur yang kamu pilih — salah satu saja.

Jalur A — Resend:

.env.local
# .env.local  (Jalur A — Resend)
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxx
EMAIL_FROM="Nama Kamu <onboarding@resend.dev>"

Jalur B — Gmail SMTP:

.env.local
# .env.local  (Jalur B — Gmail SMTP gratis)
GMAIL_USER=emailkamu@gmail.com
GMAIL_APP_PASSWORD=abcdefghijklmnop
EMAIL_FROM="Nama Kamu <emailkamu@gmail.com>"
Jangan pernah commit .env.local ke Git. Pastikan ada di .gitignore. Saat deploy ke Vercel, tambahkan nilai yang sama di Project Settings → Environment Variables.
7

Tulis kode pengirim email (lib/email.ts)

Buat satu file lib/email.ts berisi fungsi sendEmail(). Seluruh aplikasi cukup memanggil fungsi ini. Pilih isi sesuai jalurmu:

Jalur A — versi Resend:

lib/email.ts
// lib/email.ts  — pengirim email pakai Resend
import { Resend } from 'resend'

const resend = new Resend(process.env.RESEND_API_KEY)

export async function sendEmail({
  to,
  subject,
  html,
}: {
  to: string
  subject: string
  html: string
}) {
  const { data, error } = await resend.emails.send({
    from: process.env.EMAIL_FROM!,
    to,
    subject,
    html,
  })

  if (error) {
    console.log('[v0] Resend error:', error)
    throw new Error(error.message)
  }
  return data
}

Jalur B — versi Gmail SMTP:

lib/email.ts
// lib/email.ts  — pengirim email pakai Gmail SMTP (gratis)
import nodemailer from 'nodemailer'

// Transport SMTP Gmail. Pakai App Password, BUKAN password Gmail biasa.
const transporter = nodemailer.createTransport({
  host: 'smtp.gmail.com',
  port: 465,
  secure: true, // 465 = SSL
  auth: {
    user: process.env.GMAIL_USER,
    pass: process.env.GMAIL_APP_PASSWORD, // 16 karakter App Password
  },
})

export async function sendEmail({
  to,
  subject,
  html,
}: {
  to: string
  subject: string
  html: string
}) {
  const info = await transporter.sendMail({
    from: process.env.EMAIL_FROM,
    to,
    subject,
    html,
  })

  console.log('[v0] Email terkirim:', info.messageId)
  return info
}
Perhatikan: fungsi sendEmail() bentuknya sama persis di kedua jalur. Jadi kalau nanti mau pindah dari Gmail ke Resend, kamu cukup ganti isi file ini tanpa mengubah kode lain.
8

Buat API route untuk kirim email

Sekarang buat endpoint app/api/send-email/route.ts yang memanggil sendEmail(). Contoh ini mengirim kode verifikasi 6 digit — pola yang sama dipakai untuk OTP, reset password, atau notifikasi.

app/api/send-email/route.ts
// app/api/send-email/route.ts
import { NextResponse } from 'next/server'
import { sendEmail } from '@/lib/email'

export async function POST(req: Request) {
  try {
    const { to } = await req.json()

    // contoh: kirim kode verifikasi 6 digit
    const code = Math.floor(100000 + Math.random() * 900000).toString()

    await sendEmail({
      to,
      subject: 'Kode verifikasi kamu',
      html: `
        <div style="font-family:sans-serif;max-width:480px;margin:auto">
          <h2>Kode verifikasi</h2>
          <p>Masukkan kode ini untuk melanjutkan:</p>
          <p style="font-size:28px;font-weight:bold;letter-spacing:6px">${code}</p>
          <p style="color:#888;font-size:13px">Berlaku 10 menit. Abaikan jika bukan kamu.</p>
        </div>
      `,
    })

    // TODO: simpan "code" ke database/redis untuk dicocokkan nanti
    return NextResponse.json({ ok: true })
  } catch (err) {
    console.log('[v0] Gagal kirim email:', err)
    return NextResponse.json({ ok: false, error: 'Gagal mengirim email' }, { status: 500 })
  }
}
Untuk autentikasi sungguhan, simpan code ke database atau Redis dengan masa berlaku, lalu cocokkan saat user memasukkannya. Jangan mengirim balik kode itu sebagai respons API.
9

Uji coba & verifikasi

Jalankan dev server (pnpm dev), lalu tembak endpoint-nya dari terminal. Ganti alamat tujuan dengan email kamu:

Terminal
# uji coba kirim email dari terminal
curl -X POST http://localhost:3000/api/send-email \
  -H "Content-Type: application/json" \
  -d '{"to":"tujuanmu@gmail.com"}'

Khusus Gmail, kamu bisa cek koneksi SMTP-nya dulu tanpa mengirim apa pun (jalankan setelah memuat env, mis. lewat pnpm dev atau node --env-file=.env.local):

Cek koneksi SMTP
# verifikasi koneksi SMTP Gmail (opsional, debug cepat)
node -e "const n=require('nodemailer');n.createTransport({host:'smtp.gmail.com',port:465,secure:true,auth:{user:process.env.GMAIL_USER,pass:process.env.GMAIL_APP_PASSWORD}}).verify().then(()=>console.log('SMTP OK')).catch(e=>console.log('SMTP ERROR',e.message))"

Selesai!

Website kamu sekarang bisa mengirim email verifikasi/OTP secara gratis. Kembali ke panduan deploy.

10

Atasi masalah umum

GejalaSolusi
Gmail: "Invalid login / 535"Pakai App Password (16 huruf tanpa spasi), bukan password biasa. Pastikan 2FA aktif.
Gmail: "Username and Password not accepted"GMAIL_USER harus email lengkap, dan App Password belum dicabut.
Email masuk folder SpamVerifikasi domain (Resend) atau kirim volume kecil & isi yang wajar (Gmail).
Resend: 403 / unauthorizedRESEND_API_KEY salah/terhapus. Buat ulang di dashboard Resend.
env undefined saat runtimeRestart dev server setelah mengubah .env.local; di Vercel set env lalu redeploy.
Connection timeout SMTPCoba port 587 dengan secure:false, atau pastikan hosting mengizinkan koneksi keluar SMTP.
Masih mentok? Cek log terminal — fungsi di atas sudah menulis console.log("[v0] ...") untuk membantu melacak error pengiriman.
Panduan DeployGratis & aman