How to build a personalized image social sharing app with Cloudinary and Next.js

Illustration of a hand holding a phone with cloudinary and NEXT.js logos
September 8, 2021


Have you seen Contentful’s event website that generates customized and shareable ticket images that we released for our annual conference Fast Forward?

As events continue to evolve in the digital landscape, you might have seen some fun and engaging personalized event tickets shared on social media for GraphQL Conf and Next.js Conf in 2021. I love this idea — not only for the fun factor. It also showcases just how many great low-cost services and capabilities exist in web development.

In this post, we’re going to build a front-end application with Next.js and Cloudinary that creates a personalized image of a ticket based on URL parameters to share on Twitter and LinkedIn.

Screenshot of the ticket montage

We’ll also configure the app to behave differently for the ticket holder and anyone else who views the ticket. The beauty of this approach is that the possibilities for dynamic image sharing are endless. Previously, I wrote about using Puppeteer to generate dynamic images for social sharing. However, building this functionality with Cloudinary was so much simpler that I’m thinking of switching to this method for the Open Graph images on my blog! 🙈

Here’s a sneak preview of what we’ll build. The name parameter in the URL provides a name to embed on the image itself via the Cloudinary API rather than being overlaid via HTML and CSS. We’ll also generate a random ticket number and configure the app to behave differently for viewers who aren’t ticket holders.

Screenshot showing how to URL provides the name embed on the generated image

The only thing you’ll need for this tutorial is an image you’d like to personalize. Let’s get started!

Sign up for Cloudinary

Cloudinary is an image and video asset management service that provides an API for customizing your media on the fly. Add text to your images, style it with colors and custom fonts, crop, rotate, resize, recolour, detect faces… it's pretty powerful!

Head on over to Cloudinary and click sign up for free.

Screenshot showing how to sign up for Cloudinary

After you’ve confirmed your email address, log in to Cloudinary and you’ll be greeted with a welcome screen.

Screenshot of the Cloudinary welcome page

Upload your assets to Cloudinary

Click on the Media Library navigation item and click Upload in the top right corner. Select your template image and after a second or two, you’ll see the new image asset in your dashboard.

Screenshot of Cloudinary after image is uploaded

Your asset will be uploaded with an auto-generated suffix. Click on the asset to open it in the preview pane to change the file name so that it’s easier for you to recognize the image name in the code later on.

Screenshot of how to manage Cloudinary assets

I also uploaded some custom fonts to Cloudinary to ensure the image personalizations were on brand for Contentful. Given that you can use a variety of Google fonts with the Cloudinary API, I won’t cover fonts in this post, but you can learn how to upload custom fonts via the Cloudinary media library from this post by Jason Lengstorf.

Now our image asset is stored safely in Cloudinary. Let’s get coding!

Create a new Next.js app

I chose Next.js for this application to harness the power of server-side rendering using URL parameters, which will power the image personalization.

To spin up a new Next.js application, run the following command in your terminal:

This command creates a new directory that includes all code to get started. The output below is what you should see after you run the command in your terminal. (I’ve truncated the output a little with ‘/* more things happen here */’ but what you’re looking for is ✨ Done!)

Screenshot of the code when creating a next app

Navigate to the root of your project directory and start the development server:

Navigate to https://localhost:3000 in your browser and you’ll see your fresh Next.js app in action.

Screenshot of the welcome page for the Next.js

Let’s build our page for the ticket!

Build your page

In a Next.js application, any JavaScript file you add to the pages directory becomes a route on the front end. You can choose to work on index.js or create a new file in the pages directory. In the final implementation, I created fast-forward.js inside the pages directory to allow for the fact that the app will be used for future events. To account for this, I made sure all requests for the index were redirected to the current event page. For the purpose of this tutorial, we’ll work on index.js and serve the generated tickets under the root URL /.

Screenshot of the index.js file tree

Start with a blank slate

Delete most of the boilerplate code from index.js until you’re left with a nice blank canvas to work with:

Configure the server-side props

The image stored in Cloudinary will be personalized with the name of the ticket holder, grabbed from a URL parameter. Here’s the URL we’ll be working with in development.

In a pure JavaScript application, you can process the URL parameter on the client-side to build the page content — but with Next.js we can use getServerSideProps() to render the page on the server using the value of the URL parameter. This prevents visitors to your page from seeing a flash of undefined content or having to show a loading state as the parameter is read by the browser. Read more about getServerSideProps() on the Next.js documentation.

Add the following getServersideProps() function to the bottom of your index.js file. This function will be called with a context object, from which we can destructure the query parameters. We’ll display the name query parameter on the ticket, and we’ll use the isShared parameter to configure how the page looks depending on whether the page has been visited by the ticket holder, or shared and visited via social media.

Next, configure the Index component to take in the name and isShared props.

Next, let’s set up a few event variables to reuse in a few places to avoid lots of copying and pasting.

Configure your event details

Set up the following variables inside your Index component: eventName, ticketAppUrl, title and description. We’ll use these values later.

Optional: generate a ticket number (if you don’t have one)

I didn’t have access to legitimate ticket numbers for the Fast Forward 2021 event, but I still wanted to include a unique-ish ticket number in the design to make the personalized tickets look more official. The code in the final implementation generates a number from any given string, and the return value is prefixed with 000. Each unique string produces a unique number — the only caveat to this method being that if more than one person named “whitep4nth3r” receives a ticket to Fast Forward, then their ticket numbers will be the same. You get the gist. 🙈 

For the purposes of this tutorial, we can use Math.random() to generate a ticket number.

Now that we’ve configured the data, we need to personalize the image using Cloudinary. Let’s get to the fun stuff!

Personalize your Cloudinary image

The Cloudinary API lets you make all sorts of image customizations via URL parameters. As an example, here’s the URL generated for my own Fast Forward ticket. Cloudinary accepts an image URL (`fastforward2021.png`) with prepended parameters separated by commas.

The URL is built up of the following:

  • Cloudinary base URL —

  • Cloudinary cloud name — devrelcontentful

  • Asset type — image/upload

  • Width — w_831

  • Height — h_466

  • Crop mode — c_fill

  • Automatic asset format selection for best browser experience — f_auto

  • Rounded corners of 20px — r_20

  • Text area width of 760px — w_760

  • Name text area crop mode — c_fit

  • Name text color (as a hex code without the #) — ffffff

  • Name text gravity — g_south_west

  • Name text position coordinates — x_46,y_239

  • Name font and size — l_text:avenirdemi.otf_48

  • Name text value — :whitep4nth3r

  • The same is repeated for the ticket number text

  • Finally, the URL ends with the name of the image as stored in Cloudinary — fastforward2021.png

Let’s take a look at some JavaScript code used to generate a URL like this. At first glance, it might look overwhelming. But, once you understand how it all pieces together, you’ll want to personalize images at every opportunity! Big thanks to Jason Lengstorf for this repository, which provided some inspiration and insight to some common gotchas when working with Cloudinary URLs. 

The function generateImageUrl() below takes a number of required and optional parameters to build up a Cloudinary image URL like we explored above, to generate a personalized image. Depending on your image and how you want it personalized, you’ll want to play around with the default input parameters of generateImageUrl(), most notably the offset values, colors, font sizes and gravity values. Note that I’ve used the font “Arial” instead of the custom font used in the URL above.

For more information on how to configure these values, refer to the Cloudinary image transformations documentation.

Finally, add an <img /> tag to your Index component and add the src and alt attributes to render your personalized image. 

Woo! We’ve got a personalized image via the Cloudinary API as a URL! Next, let’s use this to show a preview of the ticket when attendees share your event on social media.

Configure Open Graph meta for social sharing

The power behind those ticket previews you see on Twitter and LinkedIn is all down to the magic of the Open Graph protocol

Screenshot of a Tweet with the generated image

The Open Graph (OG) protocol was created at Facebook in 2010 to enable web page links to become rich objects with similar functionality and appearance to other content posted on Facebook. 

Open Graph meta tags are used in the <head> of an HTML page to expose information about web pages to social media platforms and other applications that unfurl URL metadata. OG meta tags are identified in the HTML by an attribute prefixed with og.  

This is an example of an Open Graph meta tag that provides a URL to an image used to represent the web page.

OG meta tags can also be used to customize the appearance of your web pages according to the platform it’s shared on. For example, Twitter rolled out their own custom implementation of this, built on the OG protocol, and the following code tells Twitter to show the large image web page previews.

The Next Head component — imported at the top of the file and rendered inside the Index component — will add the meta tags we define inside it to the head of the resulting HTML page. 

Define an ogUrl variable above the return statement of the Index component as ${ticketAppUrl}?name=${name}&shared=true. Notice that we’re adding a second URL parameter onto the end of the URL — shared — which we configured in getSeverSideProps() earlier. This will become important in the next couple of steps.

Add the relevant OG meta tags inside the Next Head component tags to enable a fancy image preview with a title and description to show on Twitter and LinkedIn. You’ll notice we’re making good use of those event configuration variables we defined earlier.

Now, let’s create those social share links for your attendees to generate some excitement for your event!

Add Twitter and LinkedIn social sharing links

This is where all of the magic we conjured above comes together. We’re going to build a Twitter Web Intent URL and LinkedIn share URL that,  when your website is live (and this is the important part!), will pull in the image you personalized via Cloudinary via the Open Graph og:image meta tag in your HTML <head>.

The code below shows examples of how to create Twitter and LinkedIn share URLs. Things to bear in mind:

  • If you want to use line breaks (/n) in your tweet, make sure you wrap your tweet text in encodeURIComponent()

  • Ensure you include &shared=true on your share URLs — you’ll see why in the next step!

  • Make sure to convert all equals (=) symbols in the LinkedIn share URL to the HTML character code %3D — otherwise the link won’t work correctly

Finally, add anchor links to the Index component below the image tag, with your configured Twitter and LinkedIn share URLs. 

There’s just one more step. Finally, let’s configure the web page for visitors to your site who clicked on a link from social media.

Configure your web page for social clicks

Remember the isShared prop we captured in getServerSideProps()? Here’s where it comes into play.

Compare my Fast Forward ticket confirmation URL with the link shared on Twitter below.

My ticket confirmation

This is the full URL with a name parameter only:

Screenshot of Salma's ticket confirmation

What people see when they click on the link in my tweet

This is the full URL with a name parameter and shared parameter:

Screenshot of the full page

Use the code below to configure a different headline and subtitle depending on the value of the isShared parameter received by the Index component. Furthermore, non-attendees of the event see a call to action to sign up for the event, rather than sharing it on social media.

That’s a wrap!

Don’t forget — if you want to make sure your Open Graph images work as expected — you’ll need to deploy your application to a live URL. Vercel makes it really easy to go live with your Next.js application in just a few seconds. Sign up to Vercel and connect your project via GitHub — and you’re away! 

Cloudinary is pretty magical, and I can’t wait to explore its possibilities even further. What’s more, I’m excited to build similar apps in the future for different events I might run for my Discord and streaming community. If you’d like to explore the code demonstrated in this post, check out the GitHub repository here (it contains zero CSS so go wild with your designs!).

And lastly, make sure to sign up to Fast Forward 2021 to receive your free ticket! We’ve got three days of events dedicated to those building the next generation of digital experiences, from developers to architects, engineers, creatives and technology enthusiasts alike. Don’t forget to share your ticket on social media — it’ll be even more fun now you know how it works! 😉

About the author

Don't miss the latest

Get updates in your inbox
Discover new insights from the Contentful developer community each month.
add-circle arrow-right remove style-two-pin-marker subtract-circle remove