This tutorial demonstrates how to create a Photogallery app with Strapi, 11ty, TailwindCSS, and Cloudinary. The images for the photogallery will be stored in Cloudinary, and Tailwind CSS will be used to create the 11ty frontend UI.
At the end of this tutorial, you should have built an app similar to this:
Here are the tutorial’s final source code frontend and backend repositories.
Before starting this tutorial, you need to have
Strapi
Strapi is a world-leading Javascript open-source headless CMS. Strapi makes it very easy to build custom APIs either REST APIs or GraphQL that can be consumed by any client or frontend framework of choice. Learn more about Strapi on the Strapi website and blog.
11ty
11ty is a very simple to use static site generator that is created with Javascript. 11ty ships with zero Javascript dependencies making it one of the most simple and powerful static site generators.
11ty also allows you to create content using a wide range of templates languages i.e. HTML, liquid, Nunjuncks, Handlebars, mustache, etc. which it converts into HTML. To learn more about 11ty, visit the 11ty website.
Tailwind CSS
Tailwind CSS is a utility-first CSS framework that allows us to add CSS directly into our HTML tags. Tailwind is very easy to customize and can be used to create any design. Visit the Tailwind website to learn more about Tailwind CSS.
Cloudinary
According to this FAQ, Cloudinary is an end-to-end image- and video management solution for websites and mobile apps, covering everything from image and video uploads, storage, manipulations, optimizations to delivery. In this tutorial, we will be storing our images in the cloud with Cloudinary for easier accessibility.
Strapi installation
Create a new Strapi project with the following command.
yarn create strapi-app my-project --quickstart
The yarn create strapi-app
command will generate a new folder in your root folder using the project’s name, this folder contains the source code of the backend. After the complete installation, you will be automatically redirected to http://localhost:1337/admin/auth/register-admin where you will be prompted to register your details for the new app.
After registration, you will be directed to the Strapi dashboard.
Cloudinary installation and set up
The next step is to install and configure Cloudinary in the Strapi backend. When you deploy your Strapi backend to an online cloud provider like Heroku or use an SQLite database, the cache will clear off after 24 hours. Cloudinary provides the perfect solution for storing your media contents to prevent them from clearing after 24 hours.
The first thing will be to retrieve Cloudinary API keys. Visit the Cloudinary website to register a free account and access the Cloudinary dashboard. The needed API keys are in the account details section of the Cloudinary dashboard.
To set up Cloudinary in the backend, open the Strapi backend folder in your terminal, and install the strapi-upload-cloudinary package.
yarn add strapi-provider-upload-cloudinary
This Strapi package will be used to automatically export our uploaded files from the Strapi to Cloudinary. Next, open the Strapi backend folder in your code editor of choice and create a new file named plugins.js
in the config
folder.
Add the following code sample to the ./config/``plugins.js
file.
1 module.exports = ({ env }) => ({
2 // ...
3 upload: {
4 provider: 'cloudinary',
5 providerOptions: {
6 cloud_name: env('CLOUDINARY_NAME'),
7 api_key: env('CLOUDINARY_KEY'),
8 api_secret: env('CLOUDINARY_SECRET'),
9 },
10 actionOptions: {
11 upload: {},
12 delete: {},
13 },
14 },
15 // ...
16 });
Create a .env
file in your root folder and add CLOUDINARY_NAME
, CLOUDINARY_KEY
and CLOUDINARY_SECRET
to the file, for example:
1 CLOUDINARY_NAME=your-cloud-name
2 CLOUDINARY_KEY=123456789101112
3 CLOUDINARY_SECRET=0ABcdEFGH_IJKLMNOp1
CLOUDINARY_NAME
is your Cloudinary cloud name, CLOUDINARY_KEY
is your Cloudinary API KEY, and CLOUDINARY_SECRET
is your Cloudinary API SECRET. You can retrieve these details in the account details section of the Cloudinary dashboard. After the complete setup, every image you upload on Strapi will automatically be stored on your Cloudinary storage and should automatically reflect on your Cloudinary dashboard.
Creating the photos collection type
The next thing to do is to create a Photo collection type for storing photos in the backend. Login to your Strapi dashboard, navigate to Content-Types Builder and click create new collection type. Set the display name to Photos and add the following fields to the collection type:
The collection type should look like this -
Click Save to register your changes and to restart the backend server. Now you can go ahead to upload images to the backend server, and also store the images in the created Photos collection type.
Make sure to add a lot of images before moving to the next step of the tutorial. You can add any amount of images you like, these images will reflect in your photo gallery app after completion. Also, note that the images you uploaded on the server should reflect automatically on your Cloudinary dashboard.
Allowing access to our backend
Finally to complete our backend setup, we need to grant access to the backend by editing user roles and permissions. This is done to enable us to make requests for the Photos collection type without the backend asking for authorization.
To do this, navigate to Settings on the Strapi dashboard. Click Roles under USERS AND PERMISSIONS PLUGIN and select Public.
Tick find and findone for the Photos collection type and click Save to register your changes.
11ty is one of the simplest static site generators to install and setup. 11ty ships with zero dependencies, and is very lightweight and fast.
To setup 11ty, create a project folder with the name of your project. Navigate to the folder and generate a package.json file.
yarn init -y
Install 11ty into the project.
yarn add @11ty/eleventy
Confirm your installation
npx @11ty/eleventy
You should get a response similar to this if the installation is successful.
1 Wrote 0 files in 0.02 seconds (v0.12.1)
Tailwind CSS installation and set up
In this step, we will add Tailwind CSS to our 11ty project. Install tailwind, autoprefixer, and postCSS into your project.
yarn add tailwindcss postcss-cli autoprefixer
Then generate a tailwind.config.js file with the following command.
npx tailwind init
Also, create a new file name postcss.config.js
, and add the following code snippets to the file.
1 module.exports = {
2 plugins: [
3 require('tailwindcss'),
4 require('autoprefixer')
5 ]
6 };
Create a css
folder, and a new file name tailwind.css
to the folder. Add the following code snippets to the css/tailwind.css
folder.
1@tailwind base;
2@tailwind components;
3@tailwind utilities;
Navigate to your package.json file, and add the following code snippets
1 "scripts": {
2 "dev": "postcss css/tailwind.css -o _site/css/tailwind.css && eleventy --serve --quiet",
3 "build": "postcss css/tailwind.css -o _site/css/tailwind.css"
4 },
Create 11ty file structure.
Since 11ty ships with zero file structure, it is up to us to create our file structure by ourselves. Create a ./src
folder, and add a _includes
and a _helper
subfolder to the ./src
folder.
Layout files will be stored in the ./src/_includes
folder, the ./src/_helper
will contain the files that will be used to import data from our backend. The app page files will be stored directly in the src
folder.
Create a .eleventy.js
file in your root folder add the following code snippets to the file.
1 const HtmlMin = require('html-minifier');
2 const ErrorOverlay = require('eleventy-plugin-error-overlay');
3
4 module.exports = eleventyConfig => {
5 eleventyConfig.setTemplateFormats(['md']);
6 eleventyConfig.addPlugin(ErrorOverlay);
7 eleventyConfig.addTransform('htmlmin', (content, outputPath) => {
8 if (outputPath.endsWith('.html')) {
9 const minified = HtmlMin.minify(content, {
10 useShortDoctype: true,
11 removeComments: true,
12 collapseWhitespace: true,
13 });
14 return minified;
15 }
16 return content;
17 });
18 return {
19 dir: {
20 input: 'src',
21 output: '_site',
22 includes: '_includes',
23 data: '_helpers',
24 },
25 jsDataFileSuffix: '.data',
26 };
27 };
Finally, install the following packages.
yarn add axios eleventy-plugin-error-overlay html-minifier
After completing this setup, the file structure in your frontend folder should look something like this -
Creating the App layout
Our Layout files will be stored in the created _includes
. 11ty gives us the ability to create content with over 10 templating languages. For this project, we will mainly be using the Liquid templating language and markdown.
To create the app’s layout, add a header.liquid
, footer.liquid
, and a layout.liquid
to the ./src/_includes
folder. The header.liquid
file will contain the header code, the footer.liquid file will contain the footer code, and the layout.liquid
file will be the main layout file.
Add the following code snippet to the ./src/_includes/header.liquid
file.
1 <header class="text-gray-200 bg-blue-900 body-font shadow w-full">
2 <div class="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
3 <nav class="flex lg:w-2/5 flex-wrap items-center text-base md:ml-auto">
4 <a href="/"
5 class="mr-5 hover:text-gray-900 cursor-pointer border-b border-transparent hover:border-blue-600">About</a>
6 <a href="/"
7 class="mr-5 hover:text-gray-900 cursor-pointer border-b border-transparent hover:border-blue-600">Images</a>
8 <a href="/" class="mr-5 hover:text-gray-900 cursor-pointer border-b border-transparent hover:border-blue-600">Blog</a>
9 </nav>
10 <a
11 class="flex order-first lg:order-first lg:w-2/5 title-font font-medium items-center lg:items-center mb-4 md:mb-0">
12 <span class="ml-3 font-bold text-3xl">Strapi Photo Gallery</span>
13 </a>
14 </div>
15 </header>
Add the following to the ./src/_includes/footer.liquid
file.
1 <footer class="h-auto w-auto bg-blue-900 pt-10 sm:mt-10 pt-10">
2 <div class="max-w-6xl mx-2 text-gray-800 flex flex-wrap justify-left inset-x-0 bottom-0 lg:mx-20">
3 <div class="p-5 w-1/2 sm:w-4/12 md:w-3/12">
4 <div class="text-md uppercase text-gray-400 font-medium mb-6">
5 Community
6 </div>
7 <a href="#" class="my-3 block text-gray-300 hover:text-gray-100 text-sm font-medium duration-700">
8 Become my friend
9 </a>
10 <a href="#" class="my-3 block text-gray-300 hover:text-gray-100 text-sm font-medium duration-700">
11 Blog
12 </a>
13 </div>
14 <div class="p-5 w-1/2 sm:w-4/12 md:w-3/12">
15 <div class="text-md uppercase text-gray-400 font-medium mb-6">
16 Contact us
17 </div>
18 <a href="" class="my-3 block text-gray-300 hover:text-gray-100 text-sm font-medium duration-700">
19 Talk to me
20 </a>
21 <a href="" class="my-3 block text-gray-300 hover:text-gray-100 text-sm font-medium duration-700">
22 Merch
23 </a>
24 <a href="" class="my-3 block text-gray-300 hover:text-gray-100 text-sm font-medium duration-700">
25 Learn photography
26 </a>
27 </div>
28 <div class="p-5 w-1/2 sm:w-4/12 md:w-3/12">
29 <div class="text-md uppercase text-gray-400 font-medium mb-6">
30 About Me
31 </div>
32 <a href="" class="my-3 block text-gray-300 hover:text-gray-100 text-sm font-medium duration-700">
33 Who am I?
34 </a>
35 </div>
36 <div class="lg:flex space-x-10 p-5 w-1/2 sm:w-4/12 md:w-3/12">
37 <div class="text-md uppercase text-gray-400 font-medium mb-6">
38 <a href="#" class="w-10 h-10 mx-1">
39 <i class="uil uil-facebook-f"></i>
40 </a>
41 </div>
42 <div class="text-md uppercase text-gray-400 font-medium mb-6">
43 <a href="#" class="w-10 h-10 mx-1">
44 <i class="uil uil-twitter-alt"></i>
45 </a>
46 </div>
47 <div class="text-md uppercase text-gray-400 font-medium mb-6">
48 <a href="#" class="w-10 h-10 mx-1">
49 <i class="uil uil-linkedin"></i>
50 </a>
51 </div>
52 </div>
53 </div>
54 <div class="pt-2">
55 <div class="flex justify-center pb-5 m-auto pt-5 border-t border-gray-500 text-gray-400 text-sm flex-col px-2 lg:px-20 md:flex-row">
56 <div class="mt-2">(c) Copyright 2021-year. Built by Sheriff</div>
57 </div>
58 </div>
59 </footer>
Finally add the following to the ./src/_includes/layout.liquid
file.
1 <html>
2 <head>
3 <title>Photo Gallery App</title>
4 <link rel="stylesheet" href="css/tailwind.css" />
5 </head>
6 <div class="text-black">
7 {% include header.liquid %}
8 </div>
9 <div>
10 <body class="text-blue-100">
11 <div>
12 {{ content }}
13 </div>
14 </body>
15 </div>
16 <div class="text-black">
17 {% include footer.liquid %}
18 </div>
19 </html>
This code basically adds the created header.liquid and footer.liquid files to the main layout.liquid using the include tag. You can learn more about Liquid tags here. We also added the tailwind.css style sheet to the head part of the layout.
Creating the Homepage
To create the homepage, add an index.md
file to the ./src
folder. Add the following code sample to the index.md
file.
1 ---
2 title: Gallery
3 layout: layout.liquid
4 ---
5
6
7 # Hello world
8
9 ### I love photos.
The most important part of this code is the frontmatter. It tells 11ty the name of the page and the page’s layout. Start the 11ty server with yarn dev
. You should see a page similar to this when you visit http://localhost:8080/. You can use the yarn build
command to generate all your website’s static files in a _site
folder for deployment.
Importing and displaying photos from the backend
To import data from the backend, add a photos.j``s
file to the ./src/_helpers
folder. Add the following code snippets to the ./src/_helpers/photos.js
file.
1 const { default: axios } = require('axios');
2
3 module.exports = async () => {
4 try {
5 const res = await axios.get('http://localhost:1337/photos');
6 return res.data;
7 } catch (error) {
8 console.error(error);
9 }
10 };
This code makes a get request to the Strapi server and returns the response. Change the ./src/index.md
code to this.
1 ---
2 title: Gallery
3 layout: layout.liquid
4 pagination:
5 data: photos
6 size: 100
7 alias: photos
8 ---
9
10 <ul>
11 <div class="container mx-auto space-y-2 lg:space-y-0 lg:gap-2 mt-10 lg:grid lg:grid-cols-3">
12 {%- for photo in photos -%}
13 <div class="w-full rounded">
14 <img src="{{ photo.Image.url }}"
15 alt="{{ photo.Description }}">
16 </div>
17 {%- endfor -%}
18 </div>
19 </ul>
The code basically collects the returned data from the photo.js
file, and display the data with liquid templates. The photo gallery app should look like this now.
By following this tutorial to the end, you should have your photo gallery app fully set up. You have also learned how to integrate your Strapi app with Cloudinary for storing your media contents on the cloud. But don’t limit yourself to just creating a photogallery app, take advantage of Strapi and 11ty to create amazing websites and apps.
Quadri Sheriff is an aspiring technical writer and a software programmer. He is a very shy individual who loves writing, coding and reading in his spare time. He is currently interested in learning more about blockchain and the Jamstack.