A modern, fully server-rendered anime listing and browsing platform built with Next.js 14 App Router. Discover and explore anime with infinite scroll, beautiful animations, and real-time data from the Shikimori API.
Live Demo: https://anime-lover.vercel.app/
- Project Overview
- Features
- Technology Stack
- Project Structure
- Getting Started
- Environment Variables
- How to Run
- Project Walkthrough
- Components Documentation
- API Integration
- Key Functionalities
- Code Examples
- Reusing Components
- Deployment
- Keywords
- Conclusion
Anime Vault is a production-ready anime discovery platform that demonstrates modern web development practices using Next.js 14's latest features. The project showcases:
- Server-Side Rendering (SSR) for optimal SEO and performance
- Server Actions for seamless server-side data fetching
- Infinite Scroll for smooth user experience
- Framer Motion animations for engaging UI
- TypeScript for type safety
- Tailwind CSS for responsive, utility-first styling
This project serves as an excellent learning resource for understanding Next.js 14 App Router, Server Components, Client Components, and modern React patterns.
- π¨ Beautiful UI/UX - Modern, dark-themed interface with smooth animations
- π Infinite Scroll - Seamless pagination with automatic loading
- π Server-Side Rendering - Fast initial page loads and SEO optimization
- π± Fully Responsive - Works perfectly on desktop, tablet, and mobile devices
- π Framer Motion Animations - Staggered card animations for visual appeal
- π Real-Time Data - Live anime data from Shikimori API
- β‘ Optimized Images - Next.js Image component with automatic optimization
- π― TypeScript - Full type safety throughout the application
- π SEO Optimized - Comprehensive metadata for search engines and social sharing
- Next.js 14 App Router architecture
- Server Actions for data fetching
- Client Components for interactivity
- Intersection Observer API for scroll detection
- Font optimization with Next.js font system
- Image optimization and lazy loading
- Next.js 14.2.35 - React framework with App Router
- React 18 - UI library
- TypeScript 5 - Type-safe JavaScript
- Tailwind CSS 3.3 - Utility-first CSS framework
- framer-motion 10.16.5 - Animation library for React
- react-intersection-observer 9.5.3 - Intersection Observer hook
- ESLint - Code linting
- PostCSS - CSS processing
- Autoprefixer - CSS vendor prefixing
- Shikimori API - Anime data source (Documentation)
anime-vault/
βββ app/ # Next.js 14 App Router directory
β βββ layout.tsx # Root layout with SEO metadata
β βββ page.tsx # Home page (Server Component)
β βββ action.tsx # Server Actions for data fetching
β βββ globals.css # Global styles and Tailwind imports
β βββ _data.ts # Static data (optional, not used in production)
β βββ favicon.ico # Site favicon
β
βββ components/ # Reusable React components
β βββ AnimeCard.tsx # Individual anime card component
β βββ LoadMore.tsx # Infinite scroll component
β βββ Hero.tsx # Hero section component
β βββ Footer.tsx # Footer component
β βββ Motion.tsx # Framer Motion wrapper
β
βββ public/ # Static assets
β βββ anime.png # Hero image
β βββ logo.svg # Logo
β βββ hero.png # Hero background
β βββ spinner.svg # Loading spinner
β βββ episodes.svg # Episodes icon
β βββ star.svg # Star rating icon
β βββ [social-icons].svg # Social media icons
β
βββ next.config.js # Next.js configuration
βββ tailwind.config.ts # Tailwind CSS configuration
βββ tsconfig.json # TypeScript configuration
βββ postcss.config.js # PostCSS configuration
βββ package.json # Dependencies and scripts
βββ README.md # This fileapp/layout.tsx- Root layout that wraps all pages, contains SEO metadataapp/page.tsx- Main home page, fetches initial anime dataapp/action.tsx- Server Actions for fetching anime from APIcomponents/AnimeCard.tsx- Reusable card component for displaying animecomponents/LoadMore.tsx- Client component handling infinite scrollnext.config.js- Configures image domains and build settings
Before you begin, ensure you have the following installed:
- Node.js 18.x or later (Download)
- npm or yarn or pnpm (comes with Node.js)
- Git (for cloning the repository)
-
Clone the repository
git clone https://github.com/yourusername/anime-vault.git cd anime-vault -
Install dependencies
npm install # or yarn install # or pnpm install
-
Run the development server
npm run dev # or yarn dev # or pnpm dev
-
Open your browser
Navigate to http://localhost:3000 to see the application.
This project does not require environment variables as it uses the public Shikimori API which doesn't require authentication. However, if you want to add environment variables for future enhancements, here's how:
-
Create a
.env.localfile in the root directory:touch .env.local
-
Add your environment variables (example for future API keys):
# .env.local NEXT_PUBLIC_API_URL=https://shikimori.one/api # NEXT_PUBLIC_API_KEY=your_api_key_here (if needed in future)
-
Access in your code:
// Server Components and Server Actions const apiUrl = process.env.NEXT_PUBLIC_API_URL; // Client Components const apiUrl = process.env.NEXT_PUBLIC_API_URL;
- Never commit
.env.local- Add it to.gitignore - Use
NEXT_PUBLIC_prefix for client-accessible variables - Use
.env.exampleto document required variables - Keep secrets server-side - Don't expose API keys to the client
# API Configuration
NEXT_PUBLIC_API_URL=https://shikimori.one/api
# Optional: Future API key (if Shikimori requires authentication)
# NEXT_PUBLIC_API_KEY=your_api_key_herenpm run devStarts the development server at http://localhost:3000 with hot-reloading enabled.
npm run build
npm startCreates an optimized production build and starts the production server.
npm run lintRuns ESLint to check for code quality issues.
This project follows the Next.js 14 App Router architecture, which introduces several key concepts:
- Server Components (default) - Run on the server, reduce bundle size
- Client Components (
"use client") - Run in the browser, enable interactivity - Server Actions (
"use server") - Server-side functions callable from client
User Request
β
app/page.tsx (Server Component)
β
app/action.tsx (Server Action) β Shikimori API
β
Returns JSX (AnimeCard components)
β
Rendered on Server β Sent to Client
β
User Scrolls β LoadMore Component (Client)
β
Intersection Observer detects scroll
β
Calls fetchAnime Server Action
β
Appends new data to page- Initial Load: Server renders
app/page.tsxwith first 8 anime - Server Action:
fetchAnime(1)fetches data from Shikimori API - Component Creation: Data is transformed into
AnimeCardcomponents - HTML Generation: Server generates HTML with all content
- Client Hydration: React hydrates the page for interactivity
- Infinite Scroll:
LoadMorecomponent handles additional pages
Location: components/AnimeCard.tsx
Type: Server Component (can be used in both Server and Client contexts)
Purpose: Displays individual anime information in a card format.
Props:
interface Prop {
anime: AnimeProp; // Anime data object
index: number; // Index for staggered animation
}Features:
- Framer Motion animations with stagger effect
- Next.js Image optimization
- Responsive design
- Displays: image, name, type, episodes, rating
Usage Example:
import AnimeCard from "@/components/AnimeCard";
<AnimeCard
anime={{
id: "1",
name: "Attack on Titan",
image: { original: "/path/to/image.jpg" },
kind: "TV",
episodes: 75,
episodes_aired: 75,
score: "9.16",
}}
index={0}
/>;Reusing in Other Projects:
- Copy
AnimeCard.tsxandMotion.tsx - Adjust the
AnimePropinterface to match your data structure - Customize styling with Tailwind classes
- Modify animation variants as needed
Location: components/LoadMore.tsx
Type: Client Component ("use client")
Purpose: Implements infinite scroll functionality.
Features:
- Intersection Observer API for scroll detection
- Automatic pagination
- Loading spinner
- State management with React hooks
How It Works:
- Uses
useInViewhook to detect when trigger element is visible - When visible, calls
fetchAnimeServer Action - Appends new data to existing state
- Shows loading spinner during fetch
Usage Example:
import LoadMore from "@/components/LoadMore";
// In your page component
<LoadMore />;Reusing in Other Projects:
- Copy
LoadMore.tsx - Update the
fetchAnimeimport to your data fetching function - Adjust the
pagevariable initialization - Customize loading spinner and grid layout
- Modify delay timing if needed
Location: components/Hero.tsx
Type: Server Component
Purpose: Displays the landing section with branding.
Features:
- Responsive layout
- Background image support
- Gradient text effect
- Logo display
Usage Example:
import Hero from "@/components/Hero";
// In layout.tsx
<Hero />;Reusing in Other Projects:
- Copy
Hero.tsx - Replace logo and images
- Update heading text
- Customize gradient class
- Adjust responsive breakpoints
Location: components/Footer.tsx
Type: Server Component
Purpose: Site footer with copyright and social links.
Features:
- Responsive flex layout
- Social media icons
- Copyright information
Usage Example:
import Footer from "@/components/Footer";
// In layout.tsx
<Footer />;Reusing in Other Projects:
- Copy
Footer.tsx - Update copyright text
- Replace social media icons
- Add/remove social links
- Customize styling
Location: components/Motion.tsx
Type: Client Component ("use client")
Purpose: Wrapper for Framer Motion animations.
Usage Example:
import { MotionDiv } from "@/components/Motion";
<MotionDiv
variants={variants}
initial="hidden"
animate="visible"
transition={{ duration: 0.5 }}
>
Content here
</MotionDiv>;Reusing in Other Projects:
- Copy
Motion.tsx - Create additional motion components (MotionSection, MotionButton, etc.)
- Use with any Framer Motion animation
Base URL: https://shikimori.one/api
Endpoint Used:
GET /animes?page={page}&limit={limit}&order=popularityParameters:
page- Page number (starts at 1)limit- Number of items per page (default: 8)order- Sort order (popularity,ranked,name, etc.)
Response Structure:
[
{
"id": 1,
"name": "Anime Name",
"image": {
"original": "/system/animes/original/1.jpg"
},
"kind": "TV",
"episodes": 24,
"episodes_aired": 24,
"score": "8.5"
}
]Location: app/action.tsx
export async function fetchAnime(page: number) {
const response = await fetch(
`https://shikimori.one/api/animes?page=${page}&limit=8&order=popularity`
);
const data = await response.json();
return data.map((anime: AnimeProp, index: number) => (
<AnimeCard key={anime.id} anime={anime} index={index} />
));
}To integrate a different API:
- Update the fetch URL in
app/action.tsx - Adjust the
AnimePropinterface to match new data structure - Update the mapping logic to transform API response
- Modify
AnimeCardcomponent if data fields change
How it works:
app/page.tsxis a Server Component by default- Data fetching happens on the server before HTML is sent
- Reduces client-side JavaScript bundle size
- Improves SEO and initial load performance
Code Example:
// app/page.tsx
async function Home() {
// This runs on the server
const data = await fetchAnime(1);
return <div>{data}</div>;
}How it works:
- Functions marked with
"use server"run on the server - Can be called directly from Client Components
- No need for API routes
- Automatic serialization of props and return values
Code Example:
// app/action.tsx
"use server";
export async function fetchAnime(page: number) {
// Server-side code
const response = await fetch(apiUrl);
return response.json();
}How it works:
- Uses
react-intersection-observerto detect scroll position - When trigger element enters viewport, fetches next page
- Appends new data to existing state
- Shows loading indicator during fetch
Code Example:
// components/LoadMore.tsx
const { ref, inView } = useInView();
useEffect(() => {
if (inView) {
fetchAnime(page).then((res) => {
setData([...data, ...res]);
});
}
}, [inView]);How it works:
- Defines animation variants (hidden/visible states)
- Uses stagger delay for cascading effects
- Triggers on viewport entry
Code Example:
const variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
<MotionDiv
variants={variants}
initial="hidden"
animate="visible"
transition={{ delay: index * 0.25 }}
>
Content
</MotionDiv>;How it works:
- Next.js Image component automatically optimizes images
- Lazy loading by default
- Responsive images with srcset
- WebP format when supported
Code Example:
<Image
src="/image.jpg"
alt="Description"
width={500}
height={300}
// or use fill for responsive containers
fill
/>// app/actions.ts
"use server";
export async function fetchAnimeDetails(id: string) {
const response = await fetch(`https://shikimori.one/api/animes/${id}`);
return response.json();
}// components/AnimeDetails.tsx
"use client";
import { fetchAnimeDetails } from "@/app/actions";
import { useState } from "react";
export function AnimeDetails({ id }: { id: string }) {
const [details, setDetails] = useState(null);
const loadDetails = async () => {
const data = await fetchAnimeDetails(id);
setDetails(data);
};
return <button onClick={loadDetails}>Load Details</button>;
}// components/Card.tsx
interface CardProps {
title: string;
description: string;
image: string;
}
export function Card({ title, description, image }: CardProps) {
return (
<div className="rounded-lg shadow-lg overflow-hidden">
<Image src={image} alt={title} width={400} height={300} />
<div className="p-4">
<h3 className="text-xl font-bold">{title}</h3>
<p className="text-gray-600">{description}</p>
</div>
</div>
);
}// app/about/page.tsx
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>This is the about page</p>
</div>
);
}Accessible at: /about
-
Copy the component files:
cp components/AnimeCard.tsx your-project/components/ cp components/Motion.tsx your-project/components/
-
Install required dependencies:
npm install framer-motion next
-
Update the interface to match your data:
export interface YourDataProp { id: string; title: string; // Changed from 'name' // ... other fields }
-
Import and use:
import AnimeCard from "@/components/AnimeCard"; <AnimeCard anime={yourData} index={0} />;
-
Copy the component:
cp components/LoadMore.tsx your-project/components/
-
Install dependencies:
npm install react-intersection-observer
-
Update the fetch function:
// Change this import import { fetchAnime } from "../app/action"; // To your fetch function import { fetchYourData } from "../app/your-action";
-
Use in your page:
import LoadMore from "@/components/LoadMore"; <LoadMore />;
You can create custom animation variants for different effects:
// components/Motion.tsx
export const fadeInUp = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
export const scaleIn = {
hidden: { opacity: 0, scale: 0.8 },
visible: { opacity: 1, scale: 1 },
};-
Push your code to GitHub
-
Import project in Vercel:
- Go to vercel.com
- Click "New Project"
- Import your GitHub repository
-
Configure build settings:
- Framework Preset: Next.js
- Build Command:
npm run build - Output Directory:
.next
-
Deploy:
- Vercel automatically detects Next.js and configures everything
- Click "Deploy"
Netlify:
npm run build
# Deploy .next folderDocker:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]- Next.js 14
- React 18
- TypeScript
- Server Components
- Client Components
- Server Actions
- App Router
- Infinite Scroll
- Framer Motion
- Tailwind CSS
- SSR (Server-Side Rendering)
- SEO Optimization
- Anime API
- Shikimori API
- Responsive Design
- Web Development
- Full-Stack Development
- Modern React Patterns
- Performance Optimization
- Image Optimization
Anime Vault demonstrates modern web development practices using Next.js 14's cutting-edge features. This project serves as an excellent learning resource for:
- Understanding Server Components vs Client Components
- Implementing Server Actions for data fetching
- Building infinite scroll functionality
- Creating smooth animations with Framer Motion
- Optimizing for SEO and performance
- Building responsive, mobile-first UIs
- Server Components reduce bundle size and improve performance
- Server Actions simplify server-side logic without API routes
- Infinite Scroll enhances user experience for large datasets
- TypeScript provides type safety and better developer experience
- Tailwind CSS enables rapid, responsive UI development
- Add search functionality
- Implement anime detail pages
- Add user favorites/bookmarks
- Create filtering and sorting options
- Add dark/light theme toggle
- Implement user authentication
- Add comments and ratings
Feel free to use this project repository and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://arnob-mahmud.vercel.app/.
Enjoy building and learning! π
Thank you! π

