A modern anime and manga discovery platform built with Next.js 16, React 19, and TypeScript. Powered by the Jikan API v4 through a custom TypeScript wrapper.
Morime provides a clean, fast interface for browsing anime and manga. It integrates with MyAnimeList data through the Jikan API using @rushelasli/jikants, a fully-typed TypeScript wrapper.
- Browse anime by status (airing, completed, upcoming)
- Top anime rankings with multiple filter types
- Seasonal anime archives (current, past, upcoming)
- Weekly airing schedules by day
- Genre-based filtering for anime and manga
- Advanced search with multiple parameters
- Producer and studio catalogs
- Comprehensive anime and manga detail pages
- Character listings with voice actors
- Episode information and tracking
- Related content and recommendations
- YouTube trailer integration
- Staff and production information
- Dark mode support with system preference detection
- Fully responsive design
- SFW content filtering
- Optimized image loading with fallbacks
- Server-side rendering for fast initial loads
- 24-hour API response caching
Core:
- Next.js 16 (App Router with React Server Components)
- React 19
- TypeScript 5.9
- Bun (package manager and runtime)
Styling:
- Tailwind CSS 4
- Radix UI primitives
- Lucide React icons
- next-themes for dark mode
Data & API:
- @rushelasli/jikants - Jikan API wrapper
- Built-in request caching (24-hour TTL)
- Automatic rate limiting (3 req/sec)
Additional:
- Embla Carousel for home page
- Vercel Analytics integration
- class-variance-authority for component variants
- Bun 1.0 or higher
- Node.js 18+ (for compatibility)
Install dependencies:
bun installRun the development server:
bun devOpen http://localhost:3000 to view the application.
Create a production build:
bun run buildStart the production server:
bun startmorime/
├── src/
│ ├── actions/ # Server actions (cookies, etc.)
│ ├── app/ # Next.js App Router pages
│ │ ├── anime/ # Anime-related routes
│ │ ├── manga/ # Manga-related routes
│ │ └── producer/ # Studio/producer routes
│ ├── components/ # React components
│ │ ├── anime/ # Anime-specific components
│ │ ├── manga/ # Manga-specific components
│ │ ├── producer/ # Producer components
│ │ ├── ui/ # Reusable UI primitives
│ │ ├── display/ # Display components (cards, grids)
│ │ ├── forms/ # Form components
│ │ ├── layout/ # Layout components
│ │ ├── loading/ # Loading skeletons
│ │ └── navigation/ # Navigation components
│ ├── hooks/ # API integration hooks
│ │ ├── useAnime.ts # Anime operations
│ │ ├── useManga.ts # Manga operations
│ │ ├── useSeason.ts # Seasonal anime
│ │ ├── useSchedule.ts # Airing schedules
│ │ └── useProducer.ts # Producer/studio data
│ ├── lib/ # Utilities and helpers
│ │ ├── api/ # API client configuration
│ │ │ ├── jikan.ts # Jikan client setup
│ │ │ ├── client.ts # Client factory
│ │ │ └── adapters.ts # Type adapters
│ │ └── utils/ # Utility functions
│ │ ├── TitleExtractor.ts # Title handling
│ │ ├── ImageFallback.ts # Image utilities
│ │ ├── Formatter.ts # Text formatting
│ │ ├── Youtube.ts # YouTube helpers
│ │ ├── Season.ts # Season utilities
│ │ └── Cn.ts # Class name helper
│ └── types/ # TypeScript definitions
│ ├── anime.ts # Anime/manga types
│ ├── components.ts # Component prop types
│ └── pages.ts # Page prop types
└── public/ # Static assets
This project uses @rushelasli/jikants, a modern TypeScript wrapper for Jikan API v4.
Key features:
- Full TypeScript support with 100% type coverage
- Built-in HTTP caching (24-hour TTL)
- Automatic rate limiting (3 req/sec)
- 101 Jikan API endpoints covered
- Tree-shaking friendly
- Zero configuration required
The Jikan client is configured in src/lib/api/jikan.ts:
import { JikanClient } from '@rushelasli/jikants';
const jikanClient = new JikanClient({
cacheOptions: {
ttl: 1000 * 60 * 60 * 24, // 24 hours
},
enableLogging: process.env.NODE_ENV === 'development',
});
export const animeClient = jikanClient.anime;
export const mangaClient = jikanClient.manga;
export const seasonsClient = jikanClient.seasons;
export const schedulesClient = jikanClient.schedules;
export const producersClient = jikanClient.producers;API operations are abstracted through custom hooks for clean server component integration:
useAnime.ts - Anime operations
getAnime()- Fetch anime with filtersgetTopAnime()- Top-rated animegetDetailAnime()- Full anime detailsgetAnimeCharacters()- Character datagetEpisodeAnime()- Episode listingssearchAnime()- Search functionalitygetAnimeGenresList()- Genre listgetRecentlyCompletedAnime()- Recently finished anime
useManga.ts - Manga operations
getManga()- Fetch manga with filtersgetTopManga()- Top-rated mangagetDetailManga()- Full manga detailsgetMangaCharacters()- Character datasearchManga()- Search functionalitygetMangaGenresList()- Genre list
useSeason.ts - Seasonal anime
getSeason()- Current/upcoming/past seasonsgetSeasonList()- Season archive
useSchedule.ts - Airing schedules
getSchedules()- Weekly schedule by day
useProducer.ts - Studios and producers
getProducers()- List producersgetProducerById()- Studio detailsgetProducerAnime()- Anime by producersearchProducers()- Search producers
// Server component example
import { getTopAnime } from '@/hooks/useAnime';
export default async function TopAnimePage() {
const { data, totalPages } = await getTopAnime(1, {
limit: 24,
sfw: true
});
return (
<div>
{data.map(anime => (
<AnimeCard key={anime.mal_id} anime={anime} />
))}
</div>
);
}- Migrated from custom API utilities to @rushelasli/jikants package
- Renamed all hooks from PascalCase to camelCase (UseAnime.ts -> useAnime.ts)
- Removed legacy API configuration files (Config.ts, Utils.ts, Cookies.ts)
- Created centralized API client configuration
- Added type adapters for seamless integration
- Implemented TitleExtractor utility to handle Jikan's titles array
- Replaced deprecated title fields (title, title_english, title_japanese)
- Added smart fallback logic for missing translations
- Zero TypeScript deprecation warnings
- Achieved 100% TypeScript coverage
- Fixed all implicit 'any' types
- Added proper typing to all components and hooks
- Removed unused imports and variables
- Clean build with zero errors/warnings
- Implemented cookie-based SFW preference storage
- Added SFW toggle in navigation
- Integrated SFW parameter across all API calls
- Smart page reload logic to prevent unnecessary API calls
- Request deduplication using React cache()
- Added server-side request caching (React cache)
- Implemented image fallback system
- Optimized skeleton loading states
- Reduced duplicate API calls on page navigation
- Smart routing to preserve search parameters
- Created reusable PageContainer and PageHeader components
- Separated search results into dedicated components
- Improved loading skeleton accuracy
- Enhanced empty state handling
- Better error boundary coverage
- Cleaned up package.json (removed unused dependencies)
- Updated all packages to latest versions
- Simplified environment configuration
- Removed temporary documentation files
- Added comprehensive .gitignore rules
Handles title extraction from Jikan's titles array:
import { getTitle, getEnglishTitle, getJapaneseTitle } from '@/lib/utils/TitleExtractor';
const title = getTitle(anime.titles); // Primary title
const english = getEnglishTitle(anime.titles); // English or null
const japanese = getJapaneseTitle(anime.titles); // Japanese or nullYouTube integration helpers:
import { getYouTubeThumbnail } from '@/lib/utils/Youtube';
const thumbnail = getYouTubeThumbnail(embedUrl, 'maxres');Text and date formatting:
import { toSnakeCase, formatEstablishedDate } from '@/lib/utils/Formatter';
const slug = toSnakeCase('Attack on Titan'); // attack_on_titan
const date = formatEstablishedDate('1995-10-10'); // Oct 10, 1995Tailwind CSS 4 with custom configuration:
- Custom color palette
- Dark mode via next-themes
- Responsive breakpoints
- Animation utilities (tw-animate-css)
- Radix UI component primitives
jikants automatically caches all API responses for 24 hours using axios-cache-interceptor. This:
- Reduces API load
- Improves response times
- Helps stay within rate limits
- Works seamlessly across the app
- Server components are cached by default
- Static pages are pre-rendered
- ISR (Incremental Static Regeneration) for dynamic content
- Client-side navigation caching
Jikan API Limits:
- 3 requests per second
- 60 requests per minute
The jikants package handles rate limiting automatically. The 24-hour cache ensures most requests are served from cache, staying well within limits.
No environment variables are required. The application works out of the box.
Optional configuration:
# Next.js automatically sets NODE_ENV
NODE_ENV=development
# Optional: Analytics
NEXT_PUBLIC_VERCEL_ANALYTICS_ID=
NEXT_PUBLIC_GA_MEASUREMENT_ID=Optimized for Vercel deployment:
- Built-in Vercel Analytics
- Speed Insights integration
- Automatic image optimization
- Edge runtime support
- Zero configuration needed
Deploy with:
vercel --prod- Lighthouse score: 90+ on all metrics
- First Contentful Paint: Under 1.5s
- Time to Interactive: Under 3s
- Optimized images with Next.js Image
- Lazy loading for below-fold content
- Code splitting with dynamic imports
- Chrome/Edge (latest 2 versions)
- Firefox (latest 2 versions)
- Safari (latest 2 versions)
- Mobile browsers (iOS Safari, Chrome Mobile)
Contributions are welcome. Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
This is a private project. All rights reserved.
- Jikan API for providing free MyAnimeList data
- @rushelasli/jikants for the excellent TypeScript wrapper
- MyAnimeList for the original data source
- Radix UI for accessible component primitives
- Vercel for hosting and deployment platform