How to Add Text Overlays With Cloudflare Images

Use Cloudflare Images text rendering in a repeatable image workflow, with font choices, layering, caching, and billing limits checked.

Monday, September 7, 2026Omid Saffari
How to Add Text Overlays With Cloudflare Images

Turn one product photo and one live string into a finished social card inside a Cloudflare Worker. The new .text() source creates a raster text layer, .draw() places it on the image, and .output() returns the final file. A fixed-layout publishing workflow can now resize, label, encode, and serve the asset in one chain, without sending the job to a browser renderer or a second image API.

The shortest working text-overlay pipeline

Start with one repeatable layout. This example makes a 1200 by 630 product card, takes the headline from ?text=, uses a fixed brand font, and returns WebP. Replace the two example.com asset URLs with URLs you control.

Add the Images binding and turn on Workers Cache in your Wrangler configuration:

Jsonc
{
  "images": {
    "binding": "IMAGES"
  },
  "cache": {
    "enabled": true
  }
}

Then use the binding in the Worker:

JavaScript
const CARD_IMAGE = "https://assets.example.com/product-card.jpg";
const BRAND_FONT = "https://assets.example.com/fonts/Brand-Bold.woff2";

export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const headline = (url.searchParams.get("text") || "AUTUMN DROP").trim();

    // This is a layout limit for this card, not Cloudflare's 1,000-character ceiling.
    if (!headline || [...headline].length > 120) {
      return new Response("Text must contain 1 to 120 characters", { status: 400 });
    }

    const source = await fetch(CARD_IMAGE);
    if (!source.ok || !source.body) {
      return new Response("Source image unavailable", { status: 502 });
    }

    const textLayer = env.IMAGES.text(headline, {
      font: { url: BRAND_FONT },
      size: 72,
      color: "#FFFFFF"
    });

    const result = await env.IMAGES
      .input(source.body)
      .transform({ width: 1200, height: 630, fit: "cover" })
      .draw(textLayer, { left: 72, bottom: 72 })
      .output({ format: "image/webp" });

    return result.response({
      headers: {
        "Cache-Control": "public, max-age=86400, stale-while-revalidate=604800"
      }
    });
  }
};

Request /card?text=Autumn%20Drop and the output is a 1200 by 630 WebP with the supplied phrase 72 pixels from the left and bottom edges. The image URL contains the changing text, so this example naturally gives each caption its own URL. In production, use stable campaign or product IDs when you can. Free-form text in a public query string can create an unbounded number of variants, which is both a cost and abuse problem.

Keep source and font origins fixed or allowlisted. The sample does not let a visitor choose an arbitrary fetch target. That boundary matters whenever a public Worker can retrieve remote assets.

What Cloudflare is actually doing

Text rasterization turns letters into pixels. Think of .text() as a small typesetting machine that prints a transparent sticker. .draw() lays that sticker onto the photograph, and .output() seals both into the final file.

That mental model explains the API. Text is not an HTML element floating above an image in the browser. Once rendered, it is part of the image pixels.

ControlWhat you provideCurrent boundary
contentThe string to renderRequired, up to 1,000 characters
fontA URL to a TTF, OTF, WOFF, or WOFF2 fileFont file up to 20 MB; fetch or parse failure returns an error
sizeFont size in pixelsDefaults to 12; Cloudflare publishes no separate minimum or maximum size
colorHEX, CSS color name, or CSS color functionDefaults to black
Text imageThe rasterized text layerUp to 4096 by 4096 pixels
Base inputRaw image bytesUp to 20 MB through .input()

The base image sets the final canvas size. Position the text with top, left, bottom, or right pixel offsets. With no position it is centered. You cannot set both left and right, or both top and bottom, on the same overlay. Opacity runs from 0 to 1, and the default composite mode is over.

You can chain several .draw() calls. Their order is the layer order, so the final draw sits on top. Cloudflare also added a text entry to the cf.image.draw array for URL-based transformations, but the binding is the better fit when the base image already arrives as raw bytes from R2, Cloudflare Images, a request body, or another fetch.

The important omission is layout intelligence. Cloudflare currently documents font, size, and color for text styling. It does not document text boxes, wrapping, auto-fit, line height, tracking, alignment, stroke, shadow, or font fallback. Your application has to decide line breaks, safe zones, and what to do when a product name is too long.

Cache the finished card, not just the source

Images binding responses are not cached automatically. Without Workers Cache, a repeated request runs the Worker and decodes and encodes the source again. Enabling the cache and setting Cache-Control lets Cloudflare serve the finished response without repeating that work.

Caching and Images billing solve different problems. Cloudflare bills the binding by unique transformations: one unique combination of source image and parameters counts once per calendar month, and repeats of that transformation do not add Images usage during the same month. Cache hits still matter because they avoid repeated Worker execution and image processing latency.

Cloudflare does not separately explain how text strings are normalized inside the uniqueness calculation. The safe planning assumption is that every distinct base image, caption, style, and output combination is a new transformation until your own usage data proves otherwise. A catalog with 2,000 products and five card sizes can therefore behave like 10,000 variants, even if each card is viewed thousands of times.

The business math changes when the layout is already yours

Cloudflare Images Free includes 5,000 unique transformations each month. On the paid plan, the first 5,000 remain included and the rest cost $0.50 per 1,000. Cloudflare's own 10,000-transformation example puts the Images transformation line at $2.50 for the month.

Bannerbear currently lists its Automate plan at $49 per month for 1,000 API credits and Scale at $149 per month for 10,000 credits. One ordinary image render consumes one credit, before multipliers for extra formats, scale, or AI layers.

The $146.50 gap is not a turnkey saving. Bannerbear sells a working design product with a template editor, integrations, and team workflow. Cloudflare supplies rendering primitives that you have to design, code, validate, monitor, and maintain. Source storage and engineering time can sit on top of the Images transformation charge; storage inside Cloudflare Images has its own price.

The decision is still sharp. If your team already owns the design rules and the image request runs on Cloudflare, a separate rendering subscription can become optional. If marketers need to move layers visually every week, the subscription is buying the editor and operating model, not expensive pixels.

Seven workflows that benefit most, ranked

1. Catalog merchandising cards

A retailer with hundreds of SKUs can keep one approved background layout and draw the current product name, price, stock note, or promotion into each card. Inventory or campaign data triggers the Worker, which returns the finished marketplace or social asset. This pays because a price change no longer creates a manual export queue, and the same approved layout can be regenerated without touching the source photo.

The best fit is bounded copy. A two-line product name and one price badge are predictable. A paragraph of promotional copy is not.

2. Publisher and CMS social cards

A publication can call one endpoint when an article is published, passing the headline or a stable article ID. The Worker fetches the hero image, renders the title, and returns the Open Graph or social card. The payoff is fewer missing or stale sharing images and no screenshot browser waiting in the publishing path.

This is especially attractive when the CMS already runs behind Cloudflare. The image can be generated at the same edge layer that serves it, with the finished URL cached for repeat shares.

3. Marketplace status images

A marketplace can stamp NEW, RESERVED, SOLD, condition, or location onto listing cards. The listing service changes a small text value, and the image endpoint produces a new visual state. Sellers do not have to edit their original photos, while buyers see status inside the image wherever the card is embedded.

The commercial payoff is accuracy. A stale status card creates support work and disappointed clicks. A deterministic overlay can change with the record.

4. Franchise and local-offer creative

A restaurant or service franchise can hold the brand composition constant while changing city, offer, date, or price by location. One campaign definition becomes many local variants. That reduces repetitive production work while keeping the risky parts, such as font, color, and placement, outside the local operator's control.

Long place names are the trap. The workflow needs measured text widths, fallback sizes, or pre-approved abbreviations before it is safe to automate.

5. Event and schedule cards

An event platform can render speaker names, session times, room numbers, or a last-minute CANCELLED state over a fixed poster. The source schedule remains the record, and card generation becomes an output step. The payoff is speed when details change close to the event, without asking a designer to reopen dozens of files.

6. Customer report snapshots

A reporting product can place a period, customer name, and headline metric onto a branded chart image for email or messaging. The Worker takes the already-rendered chart as its base, adds the few labels that vary, and returns a compact final format. This is useful when the destination cannot run the dashboard itself.

Do not use the text layer to draw a whole dashboard. It is strongest as the last deterministic labeling step.

7. Internal operational labels

A warehouse, field-service team, or moderation queue can render short status text onto item photos: inspection state, batch, route, or review outcome. The visual travels well through systems that strip surrounding metadata. The payoff is fewer lookups when someone receives the image out of context.

This is operational convenience, not a substitute for the underlying record. Keep the status in the database and treat the image as a view.

Three products worth building

Best opportunity: a vertical social-card endpoint

Build a brand-safe card service for publishers, agencies, or multi-location businesses that already have fixed layouts but do not want to operate a browser-rendering stack. The buyer supplies a hero image and approved copy fields; the service returns stable URLs for a small set of aspect ratios.

US keyword data estimates 480 monthly searches for social media post maker, with a $19.95 cost per click. The exact phrase is broader than an API buyer, but the unusually high CPC shows that vendors compete for this production job. Bannerbear's $49 and $149 monthly tiers provide a current budget anchor for teams already paying for automated rendering.

The smallest sellable version is one Worker, three locked card layouts, an admin preview, a CMS webhook, strict source allowlists, stable cache keys, and a failure queue for text that does not fit. Sell the reliability of the publishing connection, not the raw .text() call.

The catch is template ownership. Cloudflare does not give the customer a visual editor, and every new layout can become service work. This is still the strongest opportunity because the new primitive removes the renderer from a recurring, commercially valuable workflow while leaving integration and brand governance as the product.

A catalog promotion-label engine

Build a feed-driven compositor for ecommerce teams that adds prices, sale states, delivery promises, or collection names to product imagery. A CSV, PIM, or commerce webhook updates the data; the service regenerates only changed variants.

Product label maker draws an estimated 320 US searches a month, carries commercial intent, and has a $6.03 CPC with high advertiser competition. That is evidence of paid demand around label creation, though much of the query includes printable labels rather than catalog graphics.

An MVP needs a product-feed connector, a handful of fixed badges, aspect-ratio presets, a preview, and rules for long names and missing prices. The catch is scope. Physical packaging brings print resolution, bleed, barcodes, and regulatory review that this raster overlay alone does not solve. Stay with promotional image labels unless those systems are part of the product.

An embedded add-text tool

Build a small editor component for an existing creator, marketplace, or community product: choose an image, enter short text, select from approved fonts and colors, position it inside a safe area, then export. Cloudflare handles the final rasterization behind the product's own interface.

Add text to image receives an estimated 6,600 US searches a month. The more transactional add text to image app receives about 320 searches and has grown 23 percent year over year in the same dataset. Demand is real, but much of it expects a free general-purpose editor.

The MVP is a constrained preview, four to six text presets, one export endpoint, and storage for the finished URL. The catch is weak defensibility. A standalone generic tool competes with mature free editors. It makes more sense as a paid feature inside a product that already owns the image, user, and publishing destination.

Where this is the wrong tool

Cloudflare Images can render and composite text. It does not decide what the text should say, make typography responsive, approve a brand layout, or provide a marketer-facing template editor. If those are the hard parts of the job, a full design automation product may still be cheaper than maintaining them yourself. For a broader look at changing whole brand systems, see the Canva AI 2.0 brand refresh test.

There are five practical stop signs:

  • Your copy needs sophisticated wrapping, auto-fit, rich text, or script-specific shaping that you have not tested with the chosen font.
  • Editors need to move layers visually and publish new templates without a developer.
  • A public endpoint would accept arbitrary source or font URLs without an allowlist.
  • You cannot bound variant creation, so random query strings could consume the 5,000-transform Free allowance and make new requests fail with error 9422.
  • Your local test depends on text rendering. The low-fidelity local Images implementation supports only width, height, rotate, and format, so use npx wrangler dev --remote for this feature.

The font and text limits are hard boundaries: a custom font can be no larger than 20 MB, rendered content can be no longer than 1,000 characters, and its raster layer can be no larger than 4096 by 4096 pixels. Those ceilings are generous for labels and headlines. They are not permission to turn the image API into a page-layout engine.

How can I create an image with text overlay?

Create a base handle with env.IMAGES.input(imageBytes), create the text layer with env.IMAGES.text(content, { font, size, color }), pass that layer to .draw(), and finish with .output({ format }). The base image controls the output dimensions.

How do I add an overlay over an image?

With the Images binding, pass an image handle or a .text() handle into .draw(overlay, options). Use top, left, bottom, or right for pixel offsets and opacity when the overlay should be translucent. Chain more .draw() calls for more layers; the last draw is on top.

How to do a text overlay?

For raw image bytes, use the Images binding workflow above. For a URL-based fetch() transformation, add a text entry to the cf.image.draw array and give it the same font, size, and color options. The binding is usually cleaner when your source is in R2, Cloudflare Images, a request body, or another fetch response.

How do I put text behind something in an image?

There is no documented automatic subject separation or simple behind switch in this text feature. Draw order controls which overlay is on top, while composite modes control how pixels combine. Putting text behind a person or product requires a separate subject mask or foreground layer that you prepare and draw after the text.

How can I animate text in a photo?

This release documents rasterized text and image compositing, not a text timeline or animation system. Use a video or animation renderer when the words need motion. Do not assume that preserving an animated input provides controls for animated typography.

If you want a reliable image endpoint built around your own catalog or publishing system, AI production systems is the right place to start.

Last Updated

Sep 7, 2026

CategoryDesign

Prefer this site in Google

Add omidsaffari.com as a preferred source in Google Search

Mark omidsaffari.com as preferred and Google lifts it in Top Stories, AI Overviews and AI Mode for you.

More from Design

View all Design articles
Newsletter

One letter, every Sunday. Working systems, not hot takes.

Build logs, working systems, and field notes from running a portfolio of AI ventures.

Weekly. No spam. Unsubscribe anytime.