Create Dynamic Images with NextJS ImageResponse

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

Professional NextJS Lessons

1.Course Intro
free
⏱️ 2:05
2.Get Setup
free
⏱️ 2:05
3.Project Template
free
⏱️ 5:45
4.Introducing FullStack React
free
⏱️ 4:57
5.App Router Reference
free
⏱️ 7:08
6.NextJS Server vs. "use client" Rendering
⏱️ 5:01
7.Nesting Server and Client Components
⏱️ 8:51
8.Client Only Components
⏱️ 3:48
9.Global CSS in NextJS
⏱️ 6:30
10.CSS Modules and SASS
⏱️ 4:15
11.Tailwind CSS
⏱️ 5:17
12.NextJS Custom Fonts
⏱️ 4:08
13.Static vs Dynamic Rendering
⏱️ 4:55
14.NextJS Link Component
⏱️ 3:20
15.NextJS Link Prefetching
⏱️ 5:49
16.Navigation Scroll with Link
⏱️ 2:02
17.Replace Navigation History
⏱️ 1:35
18.NextJS Image Component
⏱️ 7:01
19.Dynamic Image URLs
⏱️ 5:34
20.Safe Colocation with Private Folders
⏱️ 2:12
21.NextJS Route Groups
⏱️ 3:07
22.Dynamic Routes
⏱️ 5:37
23.NextJS API Routes
⏱️ 6:03
24.NextRequest and NextResponse
⏱️ 2:41
25.Cookies in API Handlers
⏱️ 4:25
26.React Suspense Simplified
⏱️ 2:23
27.Power of Suspense Boundaries
⏱️ 3:52
28.use Promises
⏱️ 3:24
29.Stream Promise Results from Server to Client
⏱️ 3:03
30.use Requires a Cached Promise
free
⏱️ 2:30
31.Suspense Error Boundaries
⏱️ 4:42
32.React Server Functions
⏱️ 4:50
33.Server Actions for Powerful Forms
⏱️ 5:35
34.useActionState to Simplify Form Actions
⏱️ 6:38
35.React Serializable Types
⏱️ 2:42
36.Create Dynamic Images with NextJS ImageResponse
⏱️ 5:54
37.Deploy NextJS Application to Production
⏱️ 3:51
38.NextJS Environment Variables Masterclass
⏱️ 3:50
39.Deploying and Managing Environment Variables
⏱️ 4:38
40.Using Databases for Dynamic Server Side State
free
⏱️ 7:34
41.Mastering NextJS Layout Files
⏱️ 4:54
42.Client State Management with NextJS
⏱️ 3:48
43.Client State Management In Action
⏱️ 8:04
44.Using Client State Management Libraries
⏱️ 5:25
45.Detecting Server vs Client Runtime and useEffect
⏱️ 4:33
46.Demystifying ISR vs SSR vs SSG
⏱️ 3:07
47.Static Site Generation or SSG with NextJS
⏱️ 3:16
48.Incremental Static Regeneration (ISR) Simplified
⏱️ 2:51
49.API Routes Caching with NextJS
⏱️ 3:05
50.Reference Guide to Caching for Modern NextJS
⏱️ 2:47
🚨 Work In Progress 🚧
Subscribe for Launch

Create Dynamic Images with NextJS ImageResponse

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

Core Concept

  • NextJS comes with an ImageResponse class that allows you to dynamically create images.
  • The image response is designed to be returned on its own as http response containing an image payload.
  • You can generate this image using JSX.

Simple Demo

In the below example we create a simple HTTP endpoint that generates an image on the fly.

Note: You can even use the API endpoint as the src for an img element to display it in your application as we have done.

app/api/route.tsx
app/page.tsx
import { ImageResponse } from "next/og";

export async function GET() {
return new ImageResponse(
(
<div
style={{
/** Take up all the space from the parent */
height: "100%",
width: "100%",

/** Center the children */
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<div style={{ fontSize: "36px" }}>Sample Image</div>
</div>
),
{
width: 300,
height: 300,
}
);
}

What is Satori?

Note that ImageResponse is not a complete browser implementation of html and css and is instead a stripped down engine inspired by react native. The engine is called satori and is free and open source on GitHub. It calls itself "Enlightened library to convert HTML and CSS to SVG"

This shouldn’t mean much in terms of feature availability (other than you need to use the style attribute). But it is something to be aware of in case you run into issues.

Custom Components

As a matter of fact the course completion certificates at BooleanArt are generated on the fly using ImageResponse. It is helpful to create utility components to make it easier to build advanced layouts.

The following example demonstrates some of the custom components we used at BooleanArt.

lib/ImageResponseComponents.tsx
app/api/route.tsx
app/page.tsx
export const ImageResponseRoot = ({ children }: React.PropsWithChildren) => {
return (
<div
style={{
/** Take up all the space from the parent */
height: "100%",
width: "100%",
/** Setup a column layout */
display: "flex",
flexDirection: "column",
/** Allow children to position themselves relatively */
position: "relative",
}}
>
{children}
</div>
);
};

export const ImageResponseFullBackgroundImg = ({ src }: { src: string }) => {
return (
<img
style={{
position: "absolute",
top: "0px",
bottom: "0px",
left: "0px",
right: "0px",
}}
src={src}
/>
);
};

export const ImageResponseCenteredText = ({
children,
}: React.PropsWithChildren) => {
return (
<div
style={{
display: "flex",
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
{children}
</div>
);
};

import path from "path";
import { promises as fs } from "fs";
export async function localImageToSrc(localPath: string): Promise<string> {
const imageBuffer = await fs.readFile(path.resolve(localPath));
const base64ImageSrc = `data:image/png;base64,${imageBuffer.toString(
"base64"
)}`;
return base64ImageSrc;
}

Using Custom Fonts

The Image response class allows you to pass in a set of custom fonts which enables expressive typography.

In the following example we use a custom loader to make it easier to load custom fonts and then use that font for a generated image.

lib/ImageResponseFonts.ts
app/api/route.tsx
app/page.tsx
import { promises as fs } from "fs";
import path from "path";

import type { ImageResponse } from "next/og";
type FontOptions = NonNullable<
NonNullable<
ConstructorParameters<typeof ImageResponse>[1]
>["fonts"]
>[number];

export const fontGagalin = "Gagalin";

export const getFontGagalin = async (): Promise<FontOptions> => {
const data = await fs.readFile(path.resolve("./public/fonts/Gagalin.otf"));
return {
name: fontGagalin,
data: data,
};
};

Passing Parameters to NextJS API Endpoints

In order to provide custom content for your generated Image you can pass in parameters to your api route.

The following example demonstrates using searchParams for an HTTP GET handler.

app/api/route.tsx
app/page.tsx
import {
ImageResponseCenteredText,
ImageResponseFullBackgroundImg,
ImageResponseRoot,
localImageToSrc,
} from "@/lib/ImageResponseComponents";
import { ImageResponse } from "next/og";
import { fontGagalin, getFontGagalin } from "@/lib/ImageResponseFonts";
import { NextRequest } from "next/server";

export async function GET(req: NextRequest) {
const message = new URL(req.url).searchParams.get("message") ?? "NO MESSAGE";

return new ImageResponse(
(
<ImageResponseRoot>
<ImageResponseFullBackgroundImg
src={await localImageToSrc("./public/images/background.png")}
/>
<ImageResponseCenteredText>
<div style={{ fontSize: "69px", fontFamily: fontGagalin }}>
{message}
</div>
</ImageResponseCenteredText>
</ImageResponseRoot>
),
{
width: 1080,
height: 1080,
fonts: [await getFontGagalin()],
}
);
}

Debugging ImageResponse Issues

A simple way to debug layout issues with ImageResponse is to pass in true for the debug property.

This is demonstrated in the below example.

app/api/route.tsx
app/page.tsx
import {
ImageResponseCenteredText,
ImageResponseFullBackgroundImg,
ImageResponseRoot,
localImageToSrc,
} from "@/lib/ImageResponseComponents";
import { ImageResponse } from "next/og";
import { fontGagalin, getFontGagalin } from "@/lib/ImageResponseFonts";
import { NextRequest } from "next/server";

export async function GET(req: NextRequest) {
const message = new URL(req.url).searchParams.get("message") ?? "NO MESSAGE";

return new ImageResponse(
(
<ImageResponseRoot>
<ImageResponseFullBackgroundImg
src={await localImageToSrc("./public/images/background.png")}
/>
<ImageResponseCenteredText>
<div style={{ fontSize: "69px", fontFamily: fontGagalin }}>
{message}
</div>
</ImageResponseCenteredText>
</ImageResponseRoot>
),
{
width: 1080,
height: 1080,
fonts: [await getFontGagalin()],
debug: true,
}
);
}

Note that if you are still struggling to debug issues, you can add a thicker border manually around the element that is giving you issues.

javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

Next JS comes with an image response class that allows you to dynamically create images. The image response is designed to be returned as its own HGTP response containing an image payload, and you can generate this image using JS six. To demonstrate, we create a new API endpoint and we bring in the image response class. From next js, we export a simple get handler and we are going to return an image response as a part of the response from this API endpoint. The image response class takes a JSX element as its first parameter.

00:31

We are going to return a simple D and start it using CSS properties. We ensure that this route dev takes up the entire space provided by the parent and we set up a flexbox layout, which is going to center its content for our content. We are going to render a simple dev that displays a sample message. The second parameter to the image response constructed takes a configuration object, for example, HA setting the width and the height of the generated image. Let's demonstrate this generated image within the browser. As you can see, we get a simple image of size 300 without text displayed in the center. We can actually use this image anywhere else

01:05

within our application as well. For example, within a route page, we render out a simple image element and point the source to this new API endpoint. And now if you visit this page within the browser, we can see our sample image being displayed nicely within a page. Note that image response is not a complete browser implementation of HTML and CSS and is instead a stripped down engine inspired by React Native. The underlying engine is a project called curi, which is free and open source on GitHub and it calls itself an enlightened library to convert HTML and CSS to SVG.

01:37

This shouldn't mean much in terms of feature availability, but it is something to be aware of in case you run into issues. As a matter of fact, the course completion certificates at Bullion Art are generated on the fly using image response, and it is helpful to create utility components to make it easier to build advanced layouts. We will create our components in a new TypeScript file and the first component that we will create is called an image response route. This is going to set up the root iv, which is going to take up the entire space from the parent, set up a nice flex box column layout, and additionally have position relative so we can absolutely position any

02:10

children using this parent. It is common to have a nice background in the generated image and for this purpose we create this component called image response. Full background image. It takes a source image prop and then generates an image element that is absolutely positioned to take up the entire space provided by the parent to displace some centage text. Within the generated image, we create a new component, which is going to use the CSS Flex box to center align its children. At this point, feel free to build any other components that you need for your specific use case. One more utility that we will build is a function that takes a local path to an image

02:42

and then converts it into a basic 64 encoded image so that it can be immediately passed as an SRC attribute for an image element. This way image response doesn't need to make an external HTP request in order to load a local image file without brand spanking new utilities created. Let's Demonstrate them within an HTP endpoint. We start off by returning an image response that contains the image response route, followed by the image response, full background image for which we'll load an image from a public folder, and then we display a nice centered text within the generated image.

03:13

The background image that we are using is 10 80 by 10 80, and that is the size of the image that we will generate with image response. Let's take a look at our generated image. Within the browser, you can see we have a nice background with some nice scented text. The image response class allows you to pass in a set of custom fonts, which enables expressive topography. We will load our custom fonts using a new utility file. Unfortunately, next JS does not expose the type definitions for its font options, but we can use the skills we learned in our TypeScript course. To infer that type.

03:44

From the image response constructor, we use the TypeScript built-in constructive parameters utility to fetch the constructor for image response. Then we pick its second parameter that is the options object. Next, we convert it to non knowable to remove undefined from one of the options. Then we read the fonts property and once more we convert it to non knowable and then read the type of the items within that array. By using a lookup type. If these concepts seem scary, you can master all of them. Within our text strip course, the font that we will be loading is called galion, and then we create a utility to load this font from our public folder

04:17

and then return an object that matches the type expected by xj, which we have stored in font options with the font loader created. Let's use this within our image response. Within our API route, we load the font name along with the font loader for our sample text. We set the font family to the name of the font, and then we provide the font file as a part of the configuration object for image response. Let's demonstrate this within the browser, and as you can see, our simple text is now stylized using our custom font. In order to provide custom content for your generated image, you can pass in parameters to your API route.

04:51

We can read URL parameters from the incoming next request. URL property. We create a new URL object and from its search pyramids we get the message per and if the search parameter is not provided, we default to no message. Next, instead of displaying sample text, we use the past in message with a search parameter wide in. Let's demonstrate this within the browser. If you don't provide a message parameter, we get no message, and if you provide a message, for example, below world, then that is the message that we see in the generated image. A simple way to debug layout issues with image response is

05:24

to pass in true for the debug property within the options object. For image response, we provide debug and set its value to true. This will add a nice border around the different elements that image response is going to use to generate the image. Let's demonstrate this. Within the browser, within the generated image, you can see a nice border around the different portions of the text, along with the very thin red border around the background image. If any of these borders are too thin for you, you can always explicitly add borders around the specific elements that you want to debug.

Professional NextJS

Professional NextJS

1.Course Intro
free
⏱️ 2:05
2.Get Setup
free
⏱️ 2:05
3.Project Template
free
⏱️ 5:45
4.Introducing FullStack React
free
⏱️ 4:57
5.App Router Reference
free
⏱️ 7:08
6.NextJS Server vs. "use client" Rendering
⏱️ 5:01
7.Nesting Server and Client Components
⏱️ 8:51
8.Client Only Components
⏱️ 3:48
9.Global CSS in NextJS
⏱️ 6:30
10.CSS Modules and SASS
⏱️ 4:15
11.Tailwind CSS
⏱️ 5:17
12.NextJS Custom Fonts
⏱️ 4:08
13.Static vs Dynamic Rendering
⏱️ 4:55
14.NextJS Link Component
⏱️ 3:20
15.NextJS Link Prefetching
⏱️ 5:49
16.Navigation Scroll with Link
⏱️ 2:02
17.Replace Navigation History
⏱️ 1:35
18.NextJS Image Component
⏱️ 7:01
19.Dynamic Image URLs
⏱️ 5:34
20.Safe Colocation with Private Folders
⏱️ 2:12
21.NextJS Route Groups
⏱️ 3:07
22.Dynamic Routes
⏱️ 5:37
23.NextJS API Routes
⏱️ 6:03
24.NextRequest and NextResponse
⏱️ 2:41
25.Cookies in API Handlers
⏱️ 4:25
26.React Suspense Simplified
⏱️ 2:23
27.Power of Suspense Boundaries
⏱️ 3:52
28.use Promises
⏱️ 3:24
29.Stream Promise Results from Server to Client
⏱️ 3:03
30.use Requires a Cached Promise
free
⏱️ 2:30
31.Suspense Error Boundaries
⏱️ 4:42
32.React Server Functions
⏱️ 4:50
33.Server Actions for Powerful Forms
⏱️ 5:35
34.useActionState to Simplify Form Actions
⏱️ 6:38
35.React Serializable Types
⏱️ 2:42
36.Create Dynamic Images with NextJS ImageResponse
⏱️ 5:54
37.Deploy NextJS Application to Production
⏱️ 3:51
38.NextJS Environment Variables Masterclass
⏱️ 3:50
39.Deploying and Managing Environment Variables
⏱️ 4:38
40.Using Databases for Dynamic Server Side State
free
⏱️ 7:34
41.Mastering NextJS Layout Files
⏱️ 4:54
42.Client State Management with NextJS
⏱️ 3:48
43.Client State Management In Action
⏱️ 8:04
44.Using Client State Management Libraries
⏱️ 5:25
45.Detecting Server vs Client Runtime and useEffect
⏱️ 4:33
46.Demystifying ISR vs SSR vs SSG
⏱️ 3:07
47.Static Site Generation or SSG with NextJS
⏱️ 3:16
48.Incremental Static Regeneration (ISR) Simplified
⏱️ 2:51
49.API Routes Caching with NextJS
⏱️ 3:05
50.Reference Guide to Caching for Modern NextJS
⏱️ 2:47
🚨 Work In Progress 🚧
Subscribe for Launch