Strapi
Posted on November 12, 2024
Introduction to Strapi's Media Library
Strapi is an open-source, Node.js-based Headless CMS that can easily build customizable APIs and manage web content with any front-end framework. One of the most prominent features it has is the Media Library plugin; with this, you can upload various media files (images, videos, documents) and organize them as well. This is great for making media-heavy apps such as galleries.
In this tutorial, you'll learn how to build an Astrojs Image Gallery using Astro and Strapi's Media Library plugin.
Here is a demo of the project we'll be building throughout this tutorial:
Tutorial Objectives
Here’s a quick summary of what we will learn in this tutorial:
- Create a back-end Strapi CMS to take care of image uploading.
- Fetch media files via Strapi’s REST API and display them in a responsive grid layout.
- Implement lazy loading, hover effects, and models for improved performance and user experience using Astro.js.
- Upload images securely from Astro frontend.
- And finally, build an Astrojs Image Gallery.
Key Features of Strapi Media Library:
One of the best things about Strapi is its powerful Media Library, which provides a smooth way to manage your media files and organize content with ease. Here are some of the main features:
- Add and manage images, videos, and documents easily via the admin panel.
- Supports local storage, AWS S3, Cloudinary, and more.
- Easily organize and label media files for quick reference.
Benefits of Integrating Strapi Media Library with Astro:
By integrating Strapi Media Library, we get the best of both worlds, Powered by Astro. Some of the advantages include:
- Manage media and content separately from the front-end.
- Handle unlimited files with cloud storage.
- Leverage Astro static generation for high-speed and optimized media delivery.
Prerequisites
Before we begin, ensure you have the following:
- Node.js v18 or higher
- A code editor installed on your machine
- Node.js package manager (npm) v6 and above
- Python
- Set up your Astro environment if you haven’t already
The complete code for this tutorial can be found in this GitHub repository. Clone the repo and follow along!
Strapi Installation
To create a new Strapi 5 project, run the following command:
npx create-strapi@latest gallery-project
The above command will prompt you to select the preferred configurations for your Strapi 5 project. Your selection should look like the screenshot below:
After selecting the above configurations, it will scaffold a new Strapi CMS project and install the necessary Node.js dependencies. Once setup is complete, create your admin account by filling out the required forms.
Configure the Strapi Media Library Plugin
Next, stop the server using Ctrl + C
and Configure the upload plugin in the config/plugins.js
file. By default, Strapi 5 provides a provider that uploads files to a local directory, which by default will be public/uploads/
in your Strapi project. Strapi 5 supports various storage options for uploads, such as Firebase, Cloudflare, Amazon S3, and Cloudinary. For simplicity, we will use the Local storage provider in this example:
export default ({ env }) => ({
upload: {
config: {
providerOptions: {
localServer: {
maxage: 300000
},
},
},
},
});
Enable Public Access for Upload API
To allow public access to upload media files, navigate to the Settings panel from the Strapi dashboard. Under USERS & PERMISSIONS PLUGIN, select Roles. Then click on Public from the roles table.
Scroll down to Upload, tick Select find, findOne, upload, and click Save.
Uploading Media Files to Strapi
Once the API access is configured, you can upload media files to the Media Library. Here’s how:
In the admin panel, click on the Media Library section from the left sidebar. Click Add new assets -> FROM COMPUTER -> Browse Files to select files from your computer.
Upload at least three image files for this demo.
Creating an Astro Project: Building an Astrojs Image Gallery
Now that Strapi is up and running with your media content, we’ll move on to setting up the front-end using Astro. Astro is a modern web framework that makes building fast websites a breeze by supporting component-based frameworks like React, Svelte, Vue, and others.
Setting Up a New Astro Project
To start, let’s create a new Astro project. Run the following commands in your terminal:
npm create astro@latest media-app
The above command will also prompt you to select the configuration for your project. For the demonstration in this tutorial, your selection should look like the screenshot below:
Then navigate into the new project directory and start the Astro development server:
cd ./my-media-app
npm run dev
This will spin up a development environment and open your site in the browser. You'll see a default Astro welcome page, but don't worry; we'll replace this with our image gallery soon!
Integrating Astro with Strapi Through REST API
Now, let’s integrate Strapi and Astro by fetching image data from Strapi’s API. Strapi exposes a flexible REST API out of the box, but you can also use GraphQL if you’ve installed the GraphQL plugin in Strapi. For this guide, we’ll use the REST API.
Now, in your Astro project, create a new API utility in the utils/api.ts
file to fetch gallery data from Strapi:
import { API_URL } from "../constants";
export async function fetchGallery() {
const response = await fetch(`${API_URL}/api/upload/files`);
if (!response.ok) {
throw new Error("Failed to fetch gallery data");
}
const data = await response.json();
console.log(data);
return data;
}
This function makes a simple HTTP GET
request to Strapi’s /gallery
endpoint. You can adjust the API_URL
if you’re hosting Strapi on a different URL. The function returns a JSON object that contains your gallery images.
Create a constant.ts
file in the src
folder to define the app content variables. Here, we'll define the API_URL
so we can reuse it across the application.
export const API_URL = "http://localhost:1337";
Setting Up a Responsive Front-end Layout for the Image Gallery
Now that we can fetch data from Strapi, let’s display it in a responsive front-end layout. We'll use a basic CSS Grid to create a gallery layout that adapts to different screen sizes.
First, update your pages/index.astro
file to create a page where the gallery will live:
---
import { fetchGallery } from "../utils/api";
import { API_URL } from "../constants";
const gallery = await fetchGallery();
interface Gallary {
url: string;
id: number;
name: string;
}
---
<html>
<head>
<title>Astro Image Gallery</title>
<style>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.gallery-item {
border: 1px solid #ccc;
padding: 10px;
text-align: center;
}
.gallery-item img{
width: 100%;
}
</style>
</head>
<body>
<h1>Image Gallery</h1>
<div class="gallery">
{
gallery.map((item: Gallary) => (
<div class="gallery-item" key={item.id as any}>
<img src={API_URL + item.url} alt={item.name} />
<p>{item.name}</p>
</div>
))
}
</div>
</body>
</html>
In the above code snippet, we use fetchGallery()
in the front matter section of the Astro page to retrieve image data. The CSS grid is used to create a responsive gallery layout where each image is displayed in a grid item. The map()
function loops through the fetched gallery data, rendering each image with its name below it.
Creating a Single Astrojs Image Gallery Page
Now, create a dynamic page to preview each image dynamically. Create an image/[id].astro
file in your pages
directory. Add the code snippet below:
---
import { API_URL } from "../../constants";
const { id } = Astro.params;
let imageData = null;
let error = null;
try {
const response = await fetch(`${API_URL}/api/upload/files/${id}`);
if (!response.ok) {
throw new Error(`Failed to fetch image data for ID ${id}`);
}
imageData = await response.json();
} catch (err: any) {
error = err.message;
}
---
<html lang="en">
<head>
<title>
{imageData ? imageData.name : "Image Not Found"} - Image Gallery
</title>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.image-container {
text-align: center;
}
.image-container img {
max-width: 100%;
height: auto;
}
.image-info {
margin-top: 20px;
}
.back-link {
display: inline-block;
margin-top: 20px;
padding: 10px 15px;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
}
.error {
color: red;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
{
error ? (
<div class="error">
<h1>Error</h1>
<p>{error}</p>
<a href="/" class="back-link">
Back to Gallery
</a>
</div>
) : (
<>
<h1>{imageData.name}</h1>
<div class="image-container">
<img src={`${API_URL}${imageData.url}`} alt={imageData.name} />
</div>
<div class="image-info">
<p>
<strong>File name:</strong> {imageData.name}
</p>
<p>
<strong>Upload date:</strong>{" "}
{new Date(imageData.createdAt).toLocaleString()}
</p>
<p>
<strong>File size:</strong> {(imageData.size / 1024).toFixed(2)}{" "}
KB
</p>
</div>
</>
)
}
<a href="/" class="back-link">Back to Gallery</a>
</div>
</body>
</html>
The code above sets up a dynamic page in an Astro project that displays a single image from Strapi’s media library based on the image's unique ID. The getStaticPaths
function automatically generates routes for each image, allowing you to click on an image and view its details on a separate page. Along with displaying the image, the page shows useful metadata like the image name, upload date, and file size. If there’s an issue fetching the image (for example, if the image doesn’t exist), an error message is displayed. A "Back to Gallery" button is also included to make navigation easy.
Then, since our code uses server-side rendering, update Astro configuration in the astro.config.mjs
file to allow it:
import { defineConfig } from 'astro/config';
// https://astro.build/config
export default defineConfig({
output: 'server',
});
Update the code in your pages/index.astro
file to be able to navigate to this new page:
...
<div class="gallery">
{
gallery.map((item: Gallary) => (
<div class="gallery-item" key={item.id}>
<a href={`/image/${item.id}`}>
<img src={API_URL + item.url} alt={item.name} />
<p>{item.name}</p>
</a>
</div>
))
}
</div>
...
Now click on any of the images to navigate to view more details about the image:
Handling Media Uploads from the Astro Frontend
Finally, let’s handle image uploads directly from the Astro front-end and send them to Strapi.
Implementing Image Uploads from Astro to Strapi
We’ll create a form in Astro that allows users to upload images. The form sends a POST
request to Strapi’s /api/upload
endpoint. Update the code in your pages/index.astro
to:
---
import { fetchGallery } from "../utils/api";
import { API_URL } from "../constants";
const gallery = await fetchGallery();
interface Gallery {
url: string;
id: number;
name: string;
}
---
<html lang="en">
<head>
<title>Astro Image Gallery</title>
<style>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.gallery-item {
border: 1px solid #ccc;
padding: 10px;
text-align: center;
}
.gallery-item img {
width: 100%;
transition: transform 0.3s ease;
}
.gallery-item img:hover {
transform: scale(1.05);
}
.gallery-header {
display: flex;
justify-content: space-between;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 8px;
width: 300px;
text-align: center;
}
.modal.show {
display: flex;
}
.close {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
font-size: 24px;
}
</style>
</head>
<body>
<div class="gallery-header">
<h1>Image Gallery</h1>
<button id="addImageBtn">Add New Image</button>
</div>
<div class="gallery">
{
gallery.map((item: Gallery) => (
<div class="gallery-item" key={item.id}>
<a href={`/image/${item.id}`}>
<img src={API_URL + item.url} alt={item.name} />
<p>{item.name}</p>
</a>
</div>
))
}
</div>
<div class="modal" id="uploadModal">
<div class="modal-content">
<span class="close" id="closeModal">×</span>
<h2>Upload New Image</h2>
<form id="uploadForm">
<input type="file" accept="image/*" id="fileInput" />
<br /><br />
<button type="submit">Upload Image</button>
</form>
</div>
</div>
<script define:vars={{ API_URL }}>
let showModal = false;
let selectedFile = null;
function toggleModal() {
showModal = !showModal;
document
.getElementById("uploadModal")
.classList.toggle("show", showModal);
}
function handleFileChange(event) {
selectedFile = event.target.files[0];
}
async function handleSubmit(event) {
event.preventDefault();
if (!selectedFile) {
alert("Please select an image to upload");
return;
}
const formData = new FormData();
formData.append("files", selectedFile);
try {
const response = await fetch(`${API_URL}/api/upload`, {
method: "POST",
body: formData,
});
console.log(response);
if (response.ok) {
alert("Image uploaded successfully!");
toggleModal();
location.reload();
} else {
alert("Failed to upload image.");
}
} catch (error) {
console.error("Error uploading image:", error);
alert("An error occurred while uploading the image.");
}
}
document
.getElementById("addImageBtn")
.addEventListener("click", toggleModal);
document
.getElementById("closeModal")
.addEventListener("click", toggleModal);
document
.getElementById("fileInput")
.addEventListener("change", handleFileChange);
document
.getElementById("uploadForm")
.addEventListener("submit", handleSubmit);
</script>
</body>
</html>
To allow users to add new images from the app, we added a modal with a file field and upload button, then added event listeners to show, close the modal, and upload the selected image to the Strapi media library.
You can now add new images to your gallery by clicking the Add New Image button.
Conclusion
In this tutorial, we have learned how to build an image gallery app with Astro.js and Strapi. With Astro and Strapi, we built an Astrojs image gallery.
With this, we have demonstrated how Strapi and Astro can be seamlessly integrated for media-rich applications.
Posted on November 12, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.