Manage your media with Cloudinary

July 9, 2021

By James Perkins

Tina now supports Cloudinary as a Media Manager

We released our public alpha just over a month ago and one thing that has come up in the feedback we have been collecting is the ability to use Tina's Media Manager. This was a core feature that gives content creators the ability to drag and drop their images or replace an image easily. We decided to start with Cloudinary, to allow users to keep their GitHub repositories lightweight.

Why did we start with Cloudinary?

Serving images for the web is not just about uploading one file in a specific format and resolution, it's way more complicated than that. Cloudinary has a powerful media API that returns optimized images. It can be used with Next Image and Next image optimization, with minimal configuration.

What formats are supported?

All image formats that are supported by Cloudinary are supported by Tina which are the following:


How to get started?

You need to install our new Cloudinary package. This package handles adding, retrieving, updating and deleting images without the need for any additional code.

yarn add next-tinacms-cloudinary
npm install next-tinacms-cloudinary

You also need to add your Cloudinary cloud name, API key and API secret from your Cloudinary account, to your .env file which you can find in your Cloudinary Dashboard.


Adding Cloudinary to your application

Now that you have installed the Tina Cloudinary package, we need to make some changes to our Tina application to add support for images. Firstly you will need to update your Tina client to include the Cloudinary package:

//Other imports
import { TinaCloudCloudinaryMediaStore } from 'next-tinacms-cloudinary'
const TinaWrapper = (props) => {
const cms = React.useMemo(() => {
return new TinaCMS({
apis: {
tina: client,
sidebar: {
placeholder: SidebarPlaceholder,
enabled: true,
}, []) = new TinaCloudCloudinaryMediaStore(client)
return (
<TinaCloudAuthWall cms={cms}>
<Inner {...props} />
// removed other code

Then we will need to update our schema to include the use of Images instead of a text field for a URL. Below contains an updated schema that would handle images, we have removed the other templates:

import { defineSchema } from 'tina-graphql-gateway-cli'
export default defineSchema({
collections: [
label: 'Blog Posts',
name: 'post',
path: 'content/posts',
templates: [
label: 'Article',
name: 'article',
fields: [
type: 'text',
label: 'Title',
name: 'title',
name: 'hero',
type: 'image',
label: 'Hero',
type: 'reference',
label: 'Author',
name: 'author',
collection: 'author',

The last part of the Cloudinary integration is an API route that handles checking to see if the user is authorized to use the API route and then handle the correct api method.

import {
} from 'next-tinacms-cloudinary/dist/handlers'
import { isAuthorized } from 'tina-cloud-next'
export const config = mediaHandlerConfig
export default createMediaHandler({
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
authorized: async (req, _res) => {
try {
const user = await isAuthorized(req)
return user && user.verified
} catch (e) {
return false

Now when you launch your application you will see a change in the sidebar, that gives you access to the media manager:

Note that developers can pass a pageSize option to the media store in order to decide how many media should be displayed per page in the manager. We'll continue improving Cloudinary integration and look into media caching later, for now, we feel it's already a huge step to make sure your content team can manage media like a pro in Tina.

Last Edited: July 9, 2021