Learn how to overlay text on your Open Graph images using the Cloudinary API and SDKs.
In October 2023, Twitter rolled out a big and controversial update, which changed how links to the content you share are displayed on the timeline. Previously, link previews would come with a large or small image, and most importantly, the image was accompanied by the headline and description of the URL, giving crucial information to readers scrolling the feed. Now, link previews that include large images are stripped of this contextual data, which has been replaced by a tiny label of the top-level domain of the link, overlaid on the bottom left-hand corner of the image.
Today, links with large image previews don’t even look like links — and this is on purpose. Twitter doesn’t want you to click away from the platform anymore. Sneaky. Additionally, this change comes with some big barriers to accessibility.
In a tweet from October 5, 2023, designer and developer Matt Eason writes: “The X/Twitter update to remove headlines from link previews has also completely broken their accessibility. The link/image can’t be tabbed to with the keyboard, and it’s been totally hidden from screen readers.” While the tech community on Twitter rushed to find quick fixes and temporary workarounds for this, others proved how this change could further exacerbate the sharing of misinformation, by posting links to articles that showed images of Elon Musk and other questionable public figures, accompanied by false, damaging, and highly NSFW headlines in the body of the tweet.
While, unfortunately, this post can’t help with fixing the accessibility issues around link sharing on Twitter, it will help you add contextual information to your images that accompany your URLs on Twitter using Cloudinary. But first, let’s take a look at what powers those images.
The Open Graph Protocol
Created at Facebook in 2010, the Open Graph (OG) protocol was designed to transform web links into visually rich content previews that looked like native Facebook content at the time. Today, you can use Open Graph meta tags in the <head>
of an HTML page to expose information about web pages to social media platforms and other applications that unfurl URL metadata — such as Twitter. OG meta tags in the source code are identified by an attribute prefixed with og
. The following code provides an image URL to be displayed when sharing links on platforms that support it:
<meta property="og:image" content="https://example.com/image.png" />
Shortly after, Twitter rolled out its own custom implementation built on the OG protocol, allowing you to configure the appearance of your web pages on Twitter specifically. On Twitter, you can choose to show a small square image by default, which appears next to the headline (page title) and description (meta description) of the page (and was unaffected by this change in October 2023), or a large landscape image, which used to be displayed above the headline and description.
To instruct Twitter to show large full-width OG images when you share a link, as opposed to the default smaller image alongside the title and description, you can use this meta tag:
<meta name="twitter:card" content="summary_large_image" />
To fix a lot of this mess, we could all just revert to using the smaller default image in our link previews on Twitter. However, most people prefer to use larger images because they’re easier to notice, and more aligned with the original visual intentions of the Open Graph protocol created by Facebook in 2010. If you’d like to continue using large images for your URLs on Twitter, here’s how Cloudinary can help.
Use Cloudinary to add text to Open Graph images for Twitter
Before image transformation APIs, Open Graph images were created manually — for each blog post, website page, or anything else people wanted to share — uploaded to a CDN (or a folder in a repo!), and referenced by URL when they were needed. As you can imagine, this doesn’t scale, especially for businesses that are adding hundreds of new pages to their websites each week (think e-commerce). Now, with Cloudinary, you can generate dynamic Open Graph images on the fly, via programmatically constructed URLs, or your SDK of choice.
The Cloudinary API offers huge flexibility in adding text to images, overlaying images on images, and further image transformations, but the quickest way to solve the problem Twitter created (if only for sighted people), is to use Cloudinary to add a page title to a base image. Here’s an Open Graph image for one of my blog posts, generated programmatically via the Cloudinary API.
Let’s look at how to overlay text on an image uploaded to Cloudinary, using URL parameters. The following assumes you’ve signed up for a free Cloudinary account, and you’re familiar with JavaScript.
Upload a base image to Cloudinary
Before writing any code, you’ll need a base image to work with. You might want to mock up what you want the finished product to look like in your image editing software of choice, to know how much space to leave on the design for text. A tactic I’ve used in the past is to find the shortest and longest page titles on my website, and work the design around those parameters (and I’ll also share a little bonus workaround to accommodate a variety of page title lengths).
Log in to Cloudinary, click Upload, and add your base image to your Cloudinary Media Library.
Cloudinary will add an auto-generated suffix to your image name. Click the image to open it in the preview pane, and change the file name using the pencil icon, so that it’s easier to use the asset name in the code. Here’s my base image uploaded to Cloudinary, which includes my name, and a stylized image that matches my website branding.
Now, let’s write some code!
The Cloudinary image URL: explained
Here’s the URL to the current Open Graph image for my website homepage, which overlays the text “Live streamer, software engineer and developer educator” onto my base image.
https://res.cloudinary.com/whitep4nth3r/image/upload/w_1200,h_630,c_fill,q_auto,f_auto/w_657,c_fit,g_south_west,co_rgb:ffffff,x_255,y_80,l_text:interblack.ttf_60:Live%20streamer%252C%20software%20engineer%20and%20developer%20educator/og_pink.png
It looks overwhelming at first glance, but each part of the URL gives Cloudinary specific instructions on how to transform your image, like so:
https://res.cloudinary.com/{cloud_name}/image/upload/{image_transformations}/{text_parameters}:{title}/image_name.png
Let’s take a look at each part of the URL, and how it works to overlay text on a base image.
The base URL and account slug
The base URL comprises the protocol https://
, the CDN subdomain res.cloudinary.com
, your cloud name (which is found and editable in Cloudinary account settings), followed by /image/upload/
, indicating we’re accessing a resource of type image
that is an upload
. Putting that all together, we have:
https://res.cloudinary.com/{cloud_name}/image/upload/
Image transformations
The next part of the URL tells Cloudinary what to do with the base image. My parameters, provided as as comma separated string, are as follows:
w_1200
tells Cloudinary to resize the width of the image to 1200px.h_630
tells Cloudinary to resize the height of the image to 630px.c_fill
s
ets the crop parameter, denoted byc_
, tofill
.q_auto
sets the quality parameter, denoted byq_
, toauto
.f_auto
sets the requested file format, denoted byf_
, toauto
, which automatically generates (if needed) and delivers an asset in the most optimal format for the requesting browser in order to minimize the file size.Indicate the end of the image transformations with a forward slash
/
.
While 1200×630 are the dimensions of my original base image, should I change my base image in the future and upload a different image size, using w_1200
and h_630
ensures that the text positioning will stay consistent without any further changes.
Text parameters
Next are the text parameters, provided as a comma-separated string. Note that these parameters can be in any order, but I’ve detailed them in the order they appear in the URL above.
w_657
sets the width of the text container to 657px.c_fit
tells Cloudinary to fit my text into the specified container and to wrap the text accordingly.g_south_west
anchors my text container to the bottom left of the image boundary for positioning with X and Y coordinates.co_rgb:ffffff
sets the text color to the hex code #ffffff (white).x_255
moves the text container 255px along the X axis.y_80
moves the text container 80px along the Y axis.l_text:interblack.ttf_60
adds the text layer, wherel_
denotes “layer,” sets the font tointerblack.ttf
, which I uploaded to my Cloudinary Media Library, and sets the font size to 60.
The text as an encoded string
Next, we’ll define the text we want to layer on the image, preceded by a colon. Given the text will be applied via the URL in this case, we’ll need to encode the string in code, so that “Live streamer, software engineer and developer education” becomes Live%20streamer%252C%20software%20engineer%20and%20developer%20educator
.
Image name
The final part of the URL is the name of your image (that you edited to a more memorable name after you uploaded it). Make sure to include the file extension; in my example, it’s og_pink.png
.
Now let’s look at how to construct this URL in plain JavaScript, without any libraries or SDKs.
Generating the image URL programatically in code
Here’s how I construct the Cloudinary image URL using JavaScript. The generateCloudinaryImageUrl t
akes a title
parameter, which is a string. To make the code easier to read, I define two arrays: imageTransformations
and titleParameters
, which are joined by a comma.
Above the titleParameters
you’ll notice the bonus workaround to accommodate different page title lengths I mentioned earlier. Here, I count the number of words in the title using a Regular Expression. My default titleSize
is 50. However, if I find 10 words or fewer, I set the titleSize to 60 to make it fill more of the image. Your implementation will depend on the font family you use, and the overall design of your Open Graph image, but it’s a good trick to ensure you account for a variety of title lengths in the code. Also note that I have switched out my custom font for a Cloudinary-supported Google font in the code.
Finally, all parameters are combined with the necessary parts of the Cloudinary image URL described above, joined via a forward slash, and returned from the function.
If you’d like to add extra information such as the author or the page description, it’s as straightforward as adding an extra text layer, and configuring the parameters accordingly.
function generateCloudinaryImageUrl(title) {
// configure image transformations
const imageTransformations = [`w_1200`, `h_630`, "c_fill", "q_auto", "f_auto"].join(",");
// if 10 words or fewer, increase font size
const wordCount = title.match(/(\w+)/g).length;
const titleSize = wordCount < 10 ? 60 : 50;
const titleParameters = [
`w_657`,
"c_fit",
"g_south_west",
"co_rgb:ffffff",
`x_255`,
`y_80`,
`l_text:Roboto_${titleSize}:${encodeURIComponent(title)}`,
].join(",");
// combine all URL parts to generate a Cloudinary URL
return [
"https://res.cloudinary.com",
"whitep4nth3r",
"image",
"upload",
imageTransformations,
titleParameters,
"og_pink.png",
].join("/");
}
My website is primarily a static site, so my Open Graph image URLs are generated at build time, and added to the relevant meta tags in the head of my static HTML pages. Your implementation may vary, but ensure you add the following meta tag to the head of your website pages:
<meta property="og:image"
content="https://your_dynamically_generated_cloudinary_url" />
And that’s it!
Streamline your workflow with Cloudinary SDKs
Now that you’re familiar with adding text to images using Cloudinary URL parameters, you may want to level up your productivity by using one of the many Cloudinary SDKs to achieve the same results in your front-end framework of choice, without needing to write your own bespoke transformation methods.
Transforming images with Cloudinary in NuxtJS
If you’re using Nuxt, Cloudinary provides a handy Open Graph image component that automatically generates and inserts the required Open Graph meta tags to the head of your pages.
Follow the installation instructions for a Nuxt project to install the dependency and add the module to the nuxt.config.ts file, and use the <CldOgImage>
component to render Open Graph meta tags containing a transformed URL. Inspect the source code and notice that the generated HTML contains a meta tag for twitter:card
set to summary_large_image
to provide large Open Graph images for Twitter.
<CldOgImage
src="og_pink.png"
height="630"
width="1200"
:overlays="[
{
position: {
gravity: 'south_west',
y: 80,
x: 255,
},
width: 657,
crop: 'fit',
text: {
color: '#ffffff',
fontFamily: 'Roboto',
fontSize: 60,
text: 'Live streamer, software engineer and developer educator',
},
},
]"
/>
With this library, you can also use a composable to construct the Cloudinary URL, should you want to use the image URL elsewhere. This gives the same result, so you don’t have to manage your own URL construction.
<script lang="ts" setup>
const { url } = useCldImageUrl({
options: {
src: "/og_pink.png",
width: 1200,
height: 630,
overlays: [
{
position: {
gravity: "south_west",
x: 255,
y: 80,
},
crop: "fit",
width: 657,
text: {
color: "#ffffff",
fontFamily: "Roboto",
fontSize: 60,
text: "Live streamer, software engineer and developer educator",
},
},
],
},
});
console.log(url);
</script>
Transforming images with Cloudinary in Next.js
f you’re using Next.js, Cloudinary offers the same component functionality as Nuxt.
Follow the installation instructions for a Next.js project to install the dependency and use the <CldOgImage>
component to render Open Graph meta tags containing a transformed URL in the same way.
import { CldOgImage } from 'next-cloudinary';
<CldOgImage
src="og_pink.png"
width="1200"
height="630"
overlays={[
{
position: {
gravity: 'south_west',
y: 80,
x: 255,
},
width: 657,
crop: 'fit',
text: {
color: '#ffffff',
fontFamily: 'Roboto',
fontSize: 60,
text: 'Live streamer, software engineer and developer educator',
},
},
]}
/>;
Image transformation in front-end frameworks and libraries
Cloudinary offers many more image transformation libraries, SDKs, and utilities for frameworks such as Svelte, Angular, and for the most popular JavaScript library on the web — jQuery. Visit the official Cloudinary SDK Guide to see the full list.
What else can you do?
Generating dynamic Open Graph images for sharing links on social sharing is only touching the surface of what Cloudinary can do with your images and media. Programmable image transformations remove so much of the manual image editing work that’s often required to create responsive and adaptive page designs, such as cropping, rounding corners, overlaying text and images such as company logos, and so much more, so you can focus on writing code and shipping features without waiting for bespoke design assets over and over again. Cloudinary also offers video transformation APIs, allowing you to crop videos, overlay images and videos on videos — all without video editing software. And this blows my mind.
Although we can’t fix Twitter accessibility, we can be more intentional about how we share links. If you do want to continue using large Open Graph images on Twitter, even if you’re overlaying text on your images, I’d recommend providing the page title and a short description in your tweets, to ensure that people using screen readers and assistive technology can access the information, and understand what they’re clicking before they visit the link.
Salma Alam-Naylor
I'm a live streamer, software engineer, and developer educator. I help developers build cool stuff with blog posts, videos, live coding and open source projects.