The Right Way to Add Images to Emails in Next.JS

Joshua Amaju - Oct 24 - - Dev Community

If you're building features like authentication or transactional emails in your Next.js app, chances are you’ll need to include images in your emails. But wait—before you assume you can add images the same way you do for regular web pages, there's a small twist when it comes to email clients.

You might think this works:

import Image from 'next/image';

export default function Page() {
  return (
    <Image
      src="/email-image.png" // From your public folder
      // Other props...
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Or maybe this:

import Image from 'next/image';
import img from '../email-image.png';

export default function Page() {
  return (
    <Image
      src={img}
      // Other props...
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Both approaches will result in your image being loaded at [host]/email-image.png or /_next/static/[hash].png in a browser. The browser knows what to do here—it infers the host (like https://acme.org), grabs the image, and displays it.

But here’s the kicker: Email clients don’t know your host. Instead of seeing a nice image, your users see... nothing. Because /_next/static/[hash].png doesn’t resolve to a valid resource, it doesn’t know what host the image should come from. Essentially, there's no complete URL that points to a real resource on the internet, so the image breaks.

What's the Fix?

Simple: Use a CDN. Upload the images you need for emails to a content delivery network (CDN) like Cloudinary. Then, reference the generated URL in your emails. Here's how it looks:

import { Img } from '@react-email/components';

export default function Page() {
  return (
    <Img
      src="https://cdn.acme.org/email-image.png"
      // Other props...
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Boom! Now the email client knows where to find your image. Crisis averted.

Hosting Your Entire public Directory on a CDN

If you want to be thorough (or you're loading many images), you can configure Next.js to serve all your static assets from a CDN.

Here's how you'd set it up in your next.config.ts:

import type { NextConfig } from 'next';
import { PHASE_DEVELOPMENT_SERVER } from 'next/constants';

export default (phase) => {
  const isDev = phase === PHASE_DEVELOPMENT_SERVER;

  const nextConfig: NextConfig = {
    assetPrefix: isDev ? undefined : 'https://cdn.acme.org',
  };

  return nextConfig;
};
Enter fullscreen mode Exit fullscreen mode

This tells Next.js to prepend the CDN's URL to all static files in production. For more details, you can check out the official Next.js documentation.

Now, when you use an image in your email component like this:

import { Img } from '@react-email/components';
import img from '../email-image.png';

export default function Page() {
  return (
    <Img
      src={img}
      // Other props...
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

It’ll automatically generate a URL like https://cdn.acme.org/_next/static/[hash].png, and your email images will load without a hitch.

. . . . . . . . . . . . .
Terabox Video Player