Create a Web Component to display a random photo from Unsplash

A tutorial to create with StencilJS your own Web Component which display a random photo from Unsplash

May 29, 2019

#webdev #javascript #showdev #stencil


Photo by Mat Reding on Unsplash

I recently integrated the Unsplash API to our upcoming open source web editor for presentations, DeckDeckGo, and I thought that sharing my small learning experience in a new blog post might maybe, who knows, help someone in the future 😉 Therefore, in the following article, I will show you how to create your own Web Component to display a random photo from Unsplash.


Our goal is to create such a component which fetch a new random photo on each browser refresh

Web Components with StencilJS

When it comes to Web Components, I’ve got a bit more experience with StencilJS as any other compilers as I’m developing DeckDeckGo with. That’s why I will use it for the purpose of this tutorial. If you never used it before, don’t be afraid, it’s super straight forward, really developer friendly and fun, give it a try!

Unsplash vs Pixabay

You might ask yourself why Unsplash instead of Pixabay or any other stock photos services? Well first of all, I only compared these two 😂 Secondly, they both have their assets but Unsplash is better suited for our needs.

The main difference in the two services is the way they provide their images. If you use Unsplash, you have to use “hotlink images”, which means that images remains hosted by them. On the other side, if you would use the Pixabay API, you would have to self host the images (see “hotlinking“ in their documentation). Furthermore, if you would use Pixabay, you would have to cache every requests for 24 hours too, which I personally find a bit annoying to implement (no offense). That being said, Unsplash needs a bit of backend or cloud functions implementation too, as you have to keep your access key secret.

Like I said above, all in all, I found Unsplash better suited for our needs. But I tried both, you could even have a look at the commit I did to migrate from one to the other 😄

Prerequisite

In order to use the Unsplash API, you would need to register yourself and your application in order to get an “Access Key”. These steps could be executed on their website https://unsplash.com/developers. It is straight forward, therefore I won’t document it in this blog post but if you would face any difficulties, don’t hesitate to ping me for any questions, I would be happy to help, if I am able to 😇

Getting started

To begin our journey, we are going to create a new Web Component using StencilJS with the command line npm init stencil.

In this particular post, I’m going to step the details. If you never kickstarted such a component before, you could have a look to the “Getting started” chapter of a previous article I wrote for more details.

Once the component created, we could open the file src/components/my component.tsx in our favorite editor and remove the default code generated by the CLI in order to have an empty class at our disposal:

import { Component, h } from '@stencil/core'; @Component({ tag: 'my-component', styleUrl: 'my-component.css', shadow: true }) export class MyComponent { }

The imported h from @stencil/core is something new related to Stencil One. If you would use a previous Stencil version, just don’t import it.

Rendering an image

Our goal is to display a random stock photo from Unsplash, so guess what, we are now going to add an image to our component. Furthermore, as we are going to fetch its URI from the Unsplash API, we are going to define a variable which should contains this value.

If you are new to StencilJS, let me try to summarize. There are no Html templates in Stencil, it uses JSX. Each component as a render function that returns what needs to be displayed. Furthermore, we are annotating our variable as State() telling the component that each modifications on this particular variable should trigger a new call of the render function.

import {Component, State, h} from '@stencil/core'; @Component({ tag: 'my-component', styleUrl: 'my-component.css', shadow: true }) export class MyComponent { @State() imgSrc: string; render() { return this.imgSrc ? <img src={this.imgSrc}/> : undefined; } }

Querying a random photo from Unsplash

Unsplash provide wrappers for their API in various languages. For Javascript they notably provide the unsplash-js wrapper. I didn’t used it neither in our application nor in this tutorial. Firstly because it would not have really reduced the amount of work for me, as my requirements were really lean, and secondly because, mostly for performances reason, I rather like to have just a few line of codes in DeckDeckGo than a dependance on a all library which offers way more features than what we need.

⚠️ Important ⚠️

For the purpose of this tutorial, I will display pieces of code where the Unsplash API is queried directly (https://api.unsplash.com/). DO NOT do that in any applications, real or not, that you would upload online. Your Unplash access key has to remain secret. Furthermore, don’t “forget” to give back credits to the authors of the photos.

Fetching a random photo

The Unsplash API exposes an endpoint “random photo”, which, guess what, would allow us to request and get a random photo from their collections 😜 Therefore we are just going to implement a function which perform this (HTTPS) getter and filter the results in order to find the hotlinked url of the image.

Unsplash provide different format (“full”, “raw”, ”regular”, “small” and “thumb”) of their images. In this example I used the “thumb” size.

private getRandomPhoto(): Promise<string> { return new Promise<string>(async (resolve) => { const searchUrl: string = 'https://api.unsplash.com/photos/random/' + `?client_id=${YOUR_ACCESS_KEY}`; try { const rawResponse: Response = await fetch(searchUrl); const response = JSON.parse(await rawResponse.text()); if (!response) { resolve(undefined); return; } resolve(response.urls && response.urls.thumb ? response.urls.thumb : undefined); } catch (err) { resolve(undefined); } }); }

We are interested to fetch automatically a photo. We could therefore hook on a component loading [lifecycle](https://stenciljs.com/docs/component lifecycle) and call our getRandomPhoto function to assign its result to the variable imgSrc we have defined before.

async componentWillLoad() { this.imgSrc = await this.getRandomPhoto(); }

Track a photo download

Our solution is almost ready but we have to add one last piece: Unsplash requires a trigger on their “download” endpoint when an image is downloaded (you could find more information about when and why this should happen in their guidelines). To call this end point we could create another (HTTPS) getter.

private registerDownload(photoId: string): Promise<void> { return new Promise<void>(async (resolve) => { const downloadUrl: string = 'https://api.unsplash.com/' + `photos/${photoId}/download/?client_id=${YOUR_ACCESS_KEY}`; try { await fetch(downloadUrl); resolve(); } catch (err) { resolve(); } }); }

Obviously, now that we have created a new function, we should call it 😆 As the endpoint requires the ID of the photo, we could call it from our previous function getRandomPhoto before we return the url of the image.

In a real implementation, I would first apply and render the url of the image and then call the download endpoint, but I thought that doing it this way for the purpose of this article would be make the code clearer to explain.

await this.registerDownload(response.id);

That’s it, our component is ready 🎉

All together

Before trying it out, let me just summarize all the component code’s at once:

import {Component, State, h} from '@stencil/core'; @Component({ tag: 'my-component', styleUrl: 'my-component.css', shadow: true }) export class MyComponent { @State() imgSrc: string; async componentWillLoad() { this.imgSrc = await this.getRandomPhoto(); } private getRandomPhoto(): Promise<string> { return new Promise<string>(async (resolve) => { const searchUrl: string = 'https://api.unsplash.com/photos/random/' + `?client_id=${YOUR_ACCESS_KEY}`; try { const rawResponse: Response = await fetch(searchUrl); const response = JSON.parse(await rawResponse.text()); if (!response) { resolve(undefined); return; } await this.registerDownload(response.id); resolve(response.urls && response.urls.thumb ? response.urls.thumb : undefined); } catch (err) { resolve(undefined); } }); } private registerDownload(photoId: string): Promise<void> { return new Promise<void>(async (resolve) => { const downloadUrl: string = 'https://api.unsplash.com/' + `photos/${photoId}/download/?client_id=${YOUR_ACCESS_KEY}`; try { await fetch(downloadUrl); resolve(); } catch (err) { resolve(); } }); } render() { return this.imgSrc ? <img src={this.imgSrc}/> : undefined; } }

Trying it out

Good we are ready to test our component. StencilJS comes with a handy local test server, therefore just run npm run start in a command line and open your browser at the address http://localhost:3333. If everything works as expected, a random stock photo should be displayed and a new one should be fetched each time you would refresh your page.

Cherry on the cake 🍒🎂

As I said in my introduction, we have integrated the Unsplash API in DeckDeckGo, our upcoming open source web editor for presentations, to ease the integration of stock photos. This implementation isn’t really that different from what we implemented in this tutorial (instead of a random photo, we are just fetching a list). Therefore, as we are open source, if you are looking for a real example of implementation, you could just have a look at our source code 😃

To infinity and beyond 🚀

David