Skip to content

RESOURCES / BLOG

Generating Movie Posters in NuxtJS

Consuming APIs and generating custom images from the data received is a wholesome task. Let us explore how we can generate dynamic movie posters for films of the TMDB database.

The completed project is available on Codesandbox.

You can find the full codebase on my Github

For this article, we will be using Nuxt.Js, an amazing Vue.Js framework. To get started, ensure you have Yarn or NPM v5.2+ or v6.1+ installed. Open your preferred working directory and run the following command:

yarn create nuxt-app nuxtjs-movie-posters
# OR
npx create-nuxt-app nuxtjs-movie-posters
# OR
npm init nuxt-app nuxtjs-movie-posters
Code language: PHP (php)

This will result in a series of various setup questions. Here are our recommended defaults:

Setup defaults

Once the setup is complete, you may enter and run your app.

cd nuxtjs-movie-posters

yarn dev
# OR
npm run dev
Code language: PHP (php)

Your app will now be running on localhost:3000.

We will be using cloudinary‘s powerful Nuxt.Js SDK to manipulate our images. Login to your Cloudinary console and copy your cloud-name. If you do not have an account, feel free to create one here. We are going to add our cloud-name to our .env file. Let’s create the file.

touch .env
Code language: CSS (css)

The .env file will house our environmental variables. These are values we do not want to be stored in our codebase.

<!-- .env -->
NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloudinary-cloud-name>
Code language: HTML, XML (xml)

We can now install the recommended Nuxt.Js plugin.

yarn add @nuxtjs/cloudinary
# OR
npm install @nuxtjs/cloudinary
Code language: CSS (css)

Add it as a module in the modules section of the nuxt.config.js file.

// nuxt.config.js
export default {
    ...
    modules: [
        '@nuxtjs/cloudinary'
    ]
    ...
}
Code language: JavaScript (javascript)

Add a cloudinary section to configure the plugin. In this section, we will be linking it to our Cloudinary account by registering our cloud-name.

export default {
    ...
    cloudinary: {
        cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
        useComponent: true
    }
}
Code language: JavaScript (javascript)

You are now ready to use Cloudinary in your project.

We will be getting the movie details from TMDB (The Movie Database). First, create an account here if you do not have one. Once registered and logged in, proceed to the API setting section to obtain your API key. You will have to answer a few questions with regards to your API consumption.

We will now add the API key to our .env file.

<!-- .env -->
NUXT_ENV_TMDB_API_KEY=<your-tmdb-api-key>
Code language: HTML, XML (xml)

First and foremost, we want our users to either search for a film of their choice or obtain a random film. Let us add a basic HTML form for this purpose.

<!-- pages/index.vue -->
<templete>
    ...
    <form @submit.prevent="search">
        <div>
          <label for="search">Search</label>
          <div>
            <input
              v-model="searchQuery"
              type="text"
              name="search"
              id="search"
              placeholder="Search for movie..."
            />
          </div>
        </div>
        <div>
          <button
            v-if="!loading"
            type="submit"
          >
            {{searchQuery ? "Search" : "Random"}}
          </button>
          <p v-if="loading">
            {{loading}}
          </p>
        </div>
    </form>
    ...
</template>
Code language: HTML, XML (xml)

The above form accepts a search input and triggers the search method when submitted. When there is no search input, the button reads Random but submits to the same method. Let us now prepare the corresponding JavaScript.

// pages/index.vue
<script>
export default {
  data(){
    return {
      loading:false,
      searchQuery: null,
      show:null,
    };
  },
  mounted(){
    this.search();
  },
  methods:{
    search(){
      Array.prototype.random = function () {
        return this[Math.floor((Math.random()*this.length))];
      }

      this.loading = "Getting search results...";
      const requestOptions ={
        method: 'GET',
        redirect: 'follow'
      };

      const requestURL = "q?";

      const alphabet = "abcdefghijklmnopqrstuvwxyz";

      const queryParams = new URLSearchParams({
          api_key: process.env.NUXT_ENV_TMDB_API_KEY,
          query: this.searchQuery ?? [...alphabet].random(),
      });

      fetch(requestURL + queryParams, requestOptions)
        .then(response => response.json())
        .then(
          response => this.show = response
                                        .results
                                        .filter(show => show.backdrop_path)
                                        .random()
            )
          .then(() => this.loading="Loading image..")
        .catch(error => alert('Error:' + error));
    },
  }
}
</script>
Code language: HTML, XML (xml)

The above search method first declares a method to get random elements from arrays. We then proceed to query the https://api.themoviedb.org/3/search/movie route with the search query and our API key. If there is no search string, a random letter is used as the search query. Once the request is successful, the results are filtered to remove those without a backdrop_path. This is the path that contains the image we will use for the poster. A random show from the remaining is assigned to the show variable in the state and the loading indicator is changed to “Loading image…” from “Getting search results…”.

To generate the poster, we will utilize the globally injected $cloudinary instance. If there is no show, return nothing. Otherwise, fetch the image from the remote source.

Once the image is fetched we overlay the title with the random text color. We place the title at the bottom of the poster which is the norm with movie posters.

// pages/index.vue
export default{
    ...
    computed:{
        posterURL(){
            if(!this.show){
                return;
            }
            return this.$cloudinary.image.fetchRemote(
                `https://image.tmdb.org/t/p/original/${this.show.backdrop_path}`,
                {
                    width:800,
                    height:800,
                    crop:"fit",
                    transformation: [
                        {
                        color:"#" + Math.floor(Math.random()*16777215).toString(16),
                        overlay: {
                            font_family: "Ultra",
                            font_weight: "bold",
                            font_size: 120,
                            text: this.show.title
                        }
                        },
                        {flags: "layer_apply", gravity: "south", y: 100}
                    ]
                }
            );
        }
    },
  ...
}
Code language: JavaScript (javascript)

We customize the title using the Ultra font, boldness and 120 as the font size. Now that our URL is ready, let us display it.

<!-- pages/index.vue -->
<template>
    ...
    <div>
      <img
        :alt="`${show.title} poster`"
        v-if="show"
        :src="posterURL"
        referrerpolicy="no-referrer"
        @load="loading = null"
        @error="search"
      />
    </div>
    ...
</template>
Code language: HTML, XML (xml)

The above HTML code displays the poster when show is not empty or negative. The no-referrer policy enables us to obtain the image from our app as the image is restricted to direct browser visits only.

Once the image is loaded, we remove the loading indicator. However, if the image loading fails, we load a different image by triggering the search all over again.

When it is all done, we should have an output similar to the following:

Final output

To learn more about how to interact with the TMDP API, feel free to browse their documentation.

You may review the Cloudinary package documentation to learn more about its capabilities as well as review the official Cloudinary docs.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free