Building a Photo Archive App with Next.js and AWS Amplify

eks cloud

Sep 10, 2025

Sep 10, 2025

7 min read

7 min read

In this blog post, we'll walk you through creating a modern photo archive application using Next.js and AWS Amplify Gen 2. This Instagram-like photo app demonstrates how to build a full-stack application with authentication, file and data storage, and scalable cloud deployment using AWS services with Amplify.

What We’re Building

Our photo archive app is a modern web application that allows users to:

  • Upload and manage photos securely

  • View their photo collection in an organized interface

  • Authenticate users with AWS Cognito

  • Store images in AWS S3 with automatic scaling

  • Store data in AWS DynamoDB.

The complete project showcases the power of AWS Amplify Gen 2’s infrastructure-as-code approach, making it easier than ever to build production-ready applications.

Why AWS Amplify Gen 2?

AWS Amplify Gen 2 represents a significant evolution in full-stack development. Unlike the previous generation, Gen 2 takes an infrastructure-as-code approach that provides:

  • Type-safe backend definitions — Define your backend resources using TypeScript

  • Better local development — Improved sandbox environments for testing

  • Simplified deployment — Streamlined CI/CD with automatic builds

  • Enhanced developer experience — Better integration with modern frameworks like Next.js

Tech Stack Overview

Here’s what powers our photo archive application:

  • Frontend: Next.js 14 with App Router and React 18

  • Backend: AWS Amplify Gen 2 (Authentication, Storage, API)

  • Styling: TailwindCSS for responsive design

  • Cloud Services: AWS S3, Cognito, DynamoDB.

Setting Up the Development Environment

Prerequisites

Before we start, make sure you have:

  • Node.js 18+ installed

  • An AWS account

  • AWS CLI setup

  • Git for version control

  • Your favorite code editor

Project Initialization

First, let’s clone the repository and install dependencies:

git clone https://github.com/mahirberkano/photo-archive-app-amplify.git
cd photo-archive-app-amplify
npm install

To run the development server:

npm run dev

Your app will be available at http://localhost:3000.

AWS Amplify Gen 2 Backend Configuration

Amplify Resource Configuration

The heart of our Amplify Gen 2 setup is the resource configuration. Here’s how we define our backend infrastructure:

// backend.ts
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
import { storage } from './storage/resource';
/**
 * @see https://docs.amplify.aws/react/build-a-backend/ to add storage, functions, and more
 */
const backend =defineBackend({
  auth,
  data,
  storage,
})

const { cfnUserPool } = backend.auth.resources.cfnResources
// an empty array denotes "email" and "phone_number" cannot be used as a username
cfnUserPool.usernameAttributes = []
// ./auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';

/**
 * Define and configure your auth resource
 * @see https://docs.amplify.aws/gen2/build-a-backend/auth
 */
export const auth = defineAuth({
  loginWith: {
    email: true,
  },
    
  userAttributes: {
    preferredUsername: {
      mutable: true,
      required: true,
    }
  }
});
// ./data/resource
import { type ClientSchema, a, defineData } from '@aws-amplify/backend';

=========================================================================*/
const schema = a.schema({
  Photo: a
    .model({
      fileName: a.string(),
      caption: a.string(),
      uploadedBy: a.string(),
      createdAt: a.datetime(),
    })
    .authorization((allow) => [allow.authenticated()]),

  Comment: a
    .model({
      id: a.id(),
      photoId: a.id(),
      text: a.string(),
      author: a.string(),
      createdAt: a.datetime(),
    })
    .authorization((allow) => [allow.authenticated()]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool', // requires Cognito login
  },
});

/*======================================================================
Go to your frontend source code. From your client-side code, generate a
Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY
WORK IN THE FRONTEND CODE FILE.)

Using JavaScript or Next.js React Server Components, Middleware, Server 
Actions or Pages Router? Review how to generate Data clients for those use
cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
=========================================================================*/

/*
"use client"
import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource";

const client = generateClient<Schema>() // use this Data client for CRUDL requests
*/

/*=======================================================================
Fetch records from the database and use them in your frontend component.
(THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.)
=========================================================================*/
// ./storage/resource.ts
import { defineStorage } from '@aws-amplify/backend';

export const storage = defineStorage({
  name: 'photoStorage',
  access: (allow) => ({
    'public/*': [
      allow.authenticated.to(['read', 'write']), // logged in users can upload + read
      allow.guest.to(['read']),                  // guests can view
    ],
  }),
});

Setting Up the Sandbox Environment

Amplify Gen 2 makes local development seamless with sandbox environments:

Before using Amplify you must setup AWS CLI to your local environment!

For more information about AWS CLI https://docs.aws.amazon.com/cli/

npx ampx sandbox

This command creates a temporary backend environment that mirrors your production setup. You’ll see the backend outputs generated in amplify_outputs.json, which contains all the configuration your frontend needs.

Backend Structure

Our backend is organized into logical modules:

amplify/
 auth/          # User authentication setup with Cognito
 storage/       # S3 bucket configuration for photos
 data/          # API and database schema in DynamoDB

Each module is defined using TypeScript, providing type safety and better developer experience.

Frontend Implementation with Next.js

Amplify Configuration

In our Next.js app, we configure Amplify to connect with our backend:

import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';

Amplify.configure(outputs);

This single line connects our frontend to all the backend services we’ve defined.

Key Features Implementation

Our photo archive app includes several key features:

Photo Upload Component The upload functionality allows users to select and upload photos directly to S3 with automatic optimization and security.

Gallery View Photos are displayed in a responsive grid layout, similar to Instagram’s interface, with lazy loading for optimal performance.

User Authentication Built-in authentication flows handle user registration, login, and session management automatically.

User Authentication with AWS Cognito

User Authentication with AWS Cognito

Profile View

Upload Page

Home Page Feed

Deployment Process

Connecting to AWS Amplify Console

The deployment process with Amplify Gen 2 is remarkably straightforward:

  1. Push to GitHub: Commit your code to a GitHub repository

  2. Connect Repository: Link your GitHub repo in the Amplify Console

  3. Configure Build Settings: Amplify automatically detects your Next.js configuration

  4. Deploy: Every commit triggers an automatic deployment

Build Configuration

Amplify Gen 2 automatically handles the build process for Next.js applications. The platform recognizes your framework and applies the appropriate build settings.


This is where your deployments are shown with the console

AWS Console Overview

Once deployed, you can see all the AWS resources created by Amplify in your AWS Console:

S3 created and shown in AWS Management Console

Cognito user pool and users shown in AWS Management Console

DynamoDB table created for comments and other IDs are shown in AWS Management Console

Resource Management

Amplify Gen 2 creates and manages various AWS services:

  • S3 Buckets for photo storage with automatic scaling

  • Cognito User Pools for authentication

  • DynamoDB for data storage such as comments.

Performance and Scalability

Our photo archive app is built for scale:

  • Automatic Image Optimization: Photos are automatically optimized for web delivery

  • CDN Distribution: CloudFront ensures fast global access

  • Serverless Architecture: Pay only for what you use with automatic scaling

  • Secure Access: Fine-grained permissions protect user data

Development Experience

Working with Amplify Gen 2 and Next.js provides several developer benefits:

  • Type Safety: Full TypeScript support across frontend and backend

  • Hot Reloading: Instant feedback during development

  • Local Testing: Complete backend simulation in sandbox mode

  • Automated Deployments: Git-based deployment workflow

Lessons Learned

Building this photo archive app taught me several valuable lessons:

  1. Infrastructure as Code: Defining backend resources in TypeScript dramatically improves maintainability

  2. Local Development: Sandbox environments make testing backend changes much faster

  3. Deployment Simplicity: Amplify’s CI/CD pipeline eliminates deployment complexity

  4. Scalability by Default: The serverless architecture scales automatically with user demand

Next Steps and Enhancements

Potential improvements for the photo archive app include:

  • Social Features: Adding likes, comments, and sharing capabilities

  • Advanced Search: Implementing photo search using AWS Rekognition

  • Mobile App: Creating React Native version using the same backend

  • Real-time Updates: Adding live photo feed updates

  • Image Processing: Automatic tagging and organization features

Conclusion

AWS Amplify Gen 2 with Next.js provides an incredibly powerful foundation for building modern web applications. The combination of type-safe backend definitions, seamless local development, and automatic scaling makes it an excellent choice for projects of any size.

The photo archive app demonstrates how quickly you can build and deploy a production-ready application with sophisticated features like user authentication, file storage, and cloud deployment.

Whether you’re a solo developer or part of a larger team, this tech stack offers the flexibility and power needed to bring your ideas to life quickly and efficiently.

Resources

Check out our medium page: Clerion Medium

Start Your Cloud Journey Today

Contact us now and take the first step toward innovation and scalability with Clerion’s expert cloud solutions.

Start Your Cloud Journey Today

Contact us now and take the first step toward innovation and scalability with Clerion’s expert cloud solutions.

Start Your Cloud Journey Today

Contact us now and take the first step toward innovation and scalability with Clerion’s expert cloud solutions.

Start Your Cloud Journey Today

Contact us now and take the first step toward innovation and scalability with Clerion’s expert cloud solutions.