## Introduction to Strapi's Media Library
[Strapi](https://strapi.io) is an open-source, Node.js-based Headless CMS that can easily build customizable APIs and manage web content with any [front-end framework](https://strapi.io/blog/comprehensive-review-of-top-javascript-frontend-frameworks). 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](https://astro.build/)** and **[Strapi's Media Library plugin](https://docs.strapi.io/user-docs/media-library)**.
Here is a demo of the project we'll be building throughout this tutorial:
![project demo](https://delicate-dawn-ac25646e6d.media.strapiapp.com/image_upload_to_strapi_with_astro_435d25afb7.gif)
### 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](https://aws.amazon.com/pm/serv-s3/), [Cloudinary](https://cloudinary.com/), 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](https://nodejs.org/en/download/package-manager)** or higher
- A code editor installed on your machine
- **Node.js package manager (npm)** v6 and above
- **Python**
- Set up your [Astro environment](https://docs.astro.build/en/install-and-setup/) if you haven’t already
> The complete code for this tutorial can be found in this [GitHub repository](https://github.com/preshenv/strapi_gallery_app). Clone the repo and follow along!
### Strapi Installation
To create a new [Strapi 5](https://strapi.io/five) project, run the following command:
```bash
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:
![strapi installation.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/strapi_installation_470b456b0b.png)
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.
![strapi admin registration.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/strapi_admin_registration_68b48bab3c.png)
## 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](https://docs.strapi.io/dev-docs/plugins/upload) 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](https://www.npmjs.com/package/@strapi/provider-upload-local) in this example:
```js
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.
![go to roles then public to allow image upload.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/go_to_roles_then_public_to_allow_image_upload_442136a1dd.png)
Scroll down to **Upload**, tick Select **find**, **findOne**, **upload**, and click **Save**.
![enable upload access in Strapi.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/enable_upload_access_in_Strapi_105af64277.png)
## 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 media files.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/upload_media_files_7a4f060e56.png)
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:
```bash
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:
![create an astro project.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/create_an_astro_project_b663b00d14.png)
Then navigate into the new project directory and start the Astro development server:
```bash
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!
![new astro app.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/new_astro_app_47197897e8.png)
## 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](https://docs.strapi.io/dev-docs/api/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:
```js
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.
```js
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:
```js
---
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.
![image gallery in Astro.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/image_gallery_84ac6fe4ed.png)
### 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:
```js
---
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:
```js
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:
```js
...
<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:
![image details page.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/image_details_page_b8f995f21c.png)
## 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:
```js
---
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.
![astro upload image modal.png](https://delicate-dawn-ac25646e6d.media.strapiapp.com/astro_upload_image_modal_d2d6bf122f.png)
## 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.