Cloudinary Blog

Cloudinary Image Gallery With Stencil Custom Components

Cloudinary Image Gallery With Stencil Custom Components

When you need to build small custom web components that can be used across all frameworks - Angular, React, Vue vanilla JS and others - Stencil is an ideal tool. Stencil enables you to create platform-independent components that can be exported as a true web component and used anywhere.

Check out our StencilJS Posts:

This article shows how to build an image gallery with Cloudinary-stored images using Stencil. Cloudinary is an end-to-end image and video management solution. The Cloudinary Image API is very easy to use and exposes image manipulation techniques via HTTP URLs. If you have never used Stencil, you can get started with this article.

Webinar
How to Optimize for Page Load Speed

Setup a Media Library and a Stencil Project

Let's start with uploading some images to the Cloudinary server and setting up a Stencil project.

Create a Cloudinary Account

We will upload images using the Cloudinary dashboard. In real projects, however, you should have a server that uses the Cloudinary SDK to upload these images.

  1. Create a Cloudinary account by signing up for the free plan, offering 300,000 images.
    Cloudinary Sign Up
  2. On your server dashboard/console, click on Media Library in the navigation bar.
  3. Use the image upload widget on the Media Library page to add images to your server.
    Cloudinary Media Library

Create a Stencil Project

The first thing we need to do with Stencil is to set up a boilerplate for a Stencil project. Fortunately, the Ionic team provides a starter template to simplify this setup process. All you need to do is clone this starter template, install the template and start your new project:

Copy to clipboard
# Clone
git clone https://github.com/ionic-team/stencil-starter.git cl-gallery

# Enter project
cd cl-gallery

# Remove original Github history
git remote rm origin

# Install dependencies
npm install

Create a Gallery Data Source

A typical gallery app or widget requires a data source. Most real situations would probably require fetching these data from a server or an API endpoint. There is no need for that in this app. We are going to create a mock data that looks like what a server would send to us.

Create db.ts in your src directory with the following content:

Full code available HERE

Just an export of an array of gallery images. Each gallery item is represented as an object with an image ID, collector/author and description about the image. The image ID (imageId) is the same ID you get when you upload an image to Cloudinary server. We can use the these IDs to fetch the images from Cloudinary.

Deliver and Transform Images with Cloudinary SDK

The browser will not understand an image ID. We need to use the Cloudinary SDK to convert image IDs to URLs. This process is known as “delivery.”

We also need to define a width and quality attribute for the image. This process is done with the SDK and referred to as “transformation.”

Before we dive into these, let's first create a container component to serve as our app shell and interact with Cloudinary. Create a folder site in the components directory with the following contents:

  • app-site.scss
  • app-site.tsx

Then update theapp-site.tsx file with the following content:

Copy to clipboard
import { Component } from '@stencil/core';
import cloudinary from 'cloudinary-core';
import data from '../../db';

@Component({
  tag: 'app-site',
  styleUrl: 'app-site.scss'
})
export class Site {

  cloudinary = null;
  galleryImages = [];

  componentWillLoad() {
    this.cloudinary = cloudinary.Cloudinary.new({
      cloud_name: ''
    })
    this.galleryImages = data.map(this.transform.bind(this));
  }

  transform(image) {
    const imageUrl =
      this.cloudinary.url(image.imageId, { width: 300, crop: "fit", quality: 'auto', secure: true });
    return Object.assign(image, { imageUrl });
  }

  render() {
    return (
      <div class="wrapper">
        <nav>
          <div>CL Gallery</div>
        </nav>
        <div class="gallery-images">
          {this.galleryImages.map(image => 
            <cl-gallery-item image={image}></cl-gallery-item>
          )}
        </div>
      </div>
    );
  }
}
  • Stencil looks very much like React because it uses JSX and has a resemblance in syntax. A component is defined as a class, but it is decorated with the Component decorator that we imported from the Stencil core above. The tag in the decorator is what is used to mount this component in a containing template, while the styleUrl is the SCSS file that defines the component's styles.
  • The class has two properties, cloudinary and galleryImages. cloudinary is going to keep a reference to the configured Cloudinary instance. This configuration happens when the component is about to be mounted in componentWillLoad. galleryImages keeps references to the array of images from our data source.
  • componentWillLoad is executed before component loads, so we are setting up Cloudinary and our data store before the component is rendered. To configure Cloudinary, pass in the cloud name from your Cloudinary server.
  • The data store is transformed before being passed to galleryImages. It's transformed with a transform method, which generates a URL and transforms the image using the object passed as second argument.
  • Finally, we use JSX to render a child component, cl-gallery-item that receives each item from the galleryImages via props.

Rendering Images with Child Component

Create another component folder, cl-gallery-item with the following files:

  • cl-gallery-item.scss
  • cl-gallery-item.tsx

The following logic should be in your TSX file:

Copy to clipboard
import { Component, Prop } from '@stencil/core';


@Component({
  tag: 'cl-gallery-item',
  styleUrl: 'cl-gallery-item.scss'
})
export class ClGalleryItem {

  @Prop() image: any;

  render() {
    return (
      <div class="cl-gallery-item">
        <img src={this.image.imageUrl} alt=""/>
        <h3>{this.image.collector}</h3>
        <p>{this.image.description}</p>
      </div>
    );
  }
}

The only change here is that we decorate a property with Prop. This property is called image and maps to the property that was passed to this component from the parent component, app-site.tsx. The property holds a gallery item, which we can then display using JSX.

Update App's Entry Point

Originally, this project has one component, which is the default component. The component is named my-name. Open index.html and you will see it's right there, so even if you keep running the app with all these changes, the visuals will never update.

Update the index.html and replace my-name component with app-site and we are good to go:

Copy to clipboard
<html dir="ltr" lang="en">
<head>
 ...
</head>
<body>

  <app-site></app-site>

</body>
</html>

Run the following command to see the app running at port 3333:

Copy to clipboard
npm start

You should see a simple grid like the one shown below:

Stencil final example

Feel free to check out the code on GitHub.

Conclusion

Stencil is quite promising and it stands out because of its ability to export components that are compatible with any other JavaScript environment. It enables you to write light components for widgets, like a Cloudinary Gallery; export the components; and import them in your Angular, React or Vue project. Cloudinary offers more than what we just discussed, including file upload, image manipulation, image optimization, and more. You can learn about all these features or get started for free.

Recent Blog Posts

New Learning Pathways From the Cloudinary Academy

In December 2019, Cloudinary launched its customer education platform, the Cloudinary Academy, replete with courses taught by the company’s experts on developer-oriented products and digital asset management (DAM) solution. The courses comprise interactive lessons and hands-on assignments, a proven way of familiarizing the audience with the course material and illustrating it with live examples.

Read more
Maya Shavin: How I Built My Website

Besides working as a senior front-end developer at Cloudinary, I'm also a content creator, a blogger, and an open-source developer. Follow me at @mayashavin and on mayashavin.com.

In the beginning, my website, mayashavin.com, was mainly for showcasing the status of my development projects and keeping me organized with my speaking schedule. Initially, I built it with Vue.js, later on switching to Nuxt.js (aka Nuxt) for a higher SEO score, and deployed it with Netlify. After some time, I added a blog section with Netlify CMS as the content management system (CMS). Everything was fine until I added more content and features, which led to a significant decline in the site’s performance. Also, the site design needed a modern look. So, I gave the site a makeover.

Read more
Automation Frees Up PetRescue’s Staff to Help Pets Find Their Forever Homes

As we spend more time at home, many of us are adopting pets for the joy, companionship and a surprising range of health benefits. In Australia, where our nonprofit customer PetRescue is located, there’s a shortage of pets to adopt. Last August, the Guardian reported that dog shelters in Australia emptied and adoption fees for puppies were running as high as $AUS1800.

Read more
Cloudinary and Contentful Make Modern Content Management Easier

I am pleased to share that Cloudinary and Contentful have joined forces to further streamline the creation, processing, and delivery of online content through Cloudinary’s digital asset management (DAM) solution and advanced transformation and delivery capabilities for images and video. What’s more, the partnership delivers a headless approach to DAM. By leveraging APIs for media management tasks, marketers and developers alike benefit from an integrated stack of optimized assets for optimization and automation. As a result, page loads are fast and beautiful, and at scale—with less overhead and effort.

Read more
Introducing Cloudinary's Nuxt Module

Since its initial release in October 2016 by the Chopin brothers as a server-side framework that runs on top of Vue.js, Nuxt (aka Nuxt.js) has gained prominence in both intuitiveness and performance. The framework offers numerous built-in features based on a modular architecture, bringing ease and simplicity to web development. Not surprisingly, Nuxt.js has seen remarkable growth in adoption by the developer community along with accolades galore. At this writing, Nuxt has earned over 30K stars on GitHub and 96 active modules with over a million downloads per month. And the upward trend is ongoing.

Read more