Content

An infinite scroll component for displaying content sections with multiple card styles and customizable layouts

πŸ“¦ Installation

pnpm dlx shadcn@latest add https://zezo-ui.vercel.app/r/zezo-content-page.json

βœ… Prerequisites

πŸ“‹ Before you start, ensure you have:

  • Installed @zezosoft/zezo-ott-api-client package
  • Installed react-infinite-scroll-component package
  • Installed swiper package (for card components)
  • Installed framer-motion package (for slider animations)
  • Configured your API client with proper authentication

πŸš€ Quick Start

The Content component displays sections of content with infinite scroll. Each section can have different styles (cards, sliders, top 10, etc.) based on the type field in your section data.

πŸ’‘ Basic Usage Example

import Content from "@/components/zezo/Content/Content";
import type { ISection, IContentData, IWhoamiData } from "@zezosoft/zezo-ott-api-client";

// 1. Prepare your sections data
const sections: ISection[] = [
  {
    data: [
      {
        _id: "section-1",
        name: "Trending Now",
        type: "normal",  // Section type (see supported types below)
        category: { _id: "1", name: "Movies", slug: "movies" },
        content: {
          data: contentArray,  // IContentData[] - your actual content items
          meta: {
            pagination: {
              page: 1,
              limit: 20,
              length: 20,
              total: 100,
              hasNextPage: true,
              nextPage: 2,
            },
          },
        },
      },
    ],
    meta: {
      pagination: {
        page: 1,
        limit: 10,
        length: 1,
        total: 10,
        hasNextPage: false,
        nextPage: null,
      },
    },
  },
];

// 2. Use the component
function ContentPage() {
  const [sectionsData, setSectionsData] = React.useState<ISection[]>(sections);
  const [isLoading, setIsLoading] = React.useState(false);
  const [hasNext, setHasNext] = React.useState(true);

  const loadMore = async () => {
    setIsLoading(true);
    try {
      const nextPage = sectionsData[0]?.meta?.pagination?.nextPage || 2;
      // Fetch next page from your API
      const response = await fetch(`/api/sections?page=${nextPage}`);
      const newData: ISection = await response.json();
      
      // Append new sections
      setSectionsData(prev => [...prev, newData]);
      setHasNext(newData.meta.pagination.hasNextPage);
    } catch (error) {
      console.error("Failed to load more:", error);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <Content
      data={sectionsData}
      isLoading={isLoading}
      hasNextPage={hasNext}
      next={loadMore}
      onClick={(item: IContentData) => {
        // Navigate to content details
        router.push(`/details/${item.type}/${item.slug}`);
      }}
      userSession={userData} // Optional: For continue watching
    />
  );
}

πŸ“Š Complete Data Example

Here's a complete example of how your IContentData should be structured:

// Example IContentData object
const exampleContent: IContentData = {
  _id: "content-123",
  name: "The Dark Knight",
  slug: "the-dark-knight",
  u_age: "18+",
  description: "When the menace known as the Joker wreaks havoc...",
  duration: "152",
  rating: 9.0,
  source_link: "https://example.com/video.m3u8",
  source_type: "HLS",
  trailer_source_link: "https://example.com/trailer.m3u8",
  trailer_source_type: "HLS",
  language: [
    { _id: "lang-1", name: "English" },
    { _id: "lang-2", name: "Hindi" }
  ],
  cast: [
    {
      _id: "cast-1",
      name: "Christian Bale",
      type: "actor",
      avatar: "https://example.com/avatar.jpg",
      castType: "lead"
    }
  ],
  poster: "https://example.com/poster.jpg",
  thumbnail: "https://example.com/thumbnail.jpg",
  views: 1000000,
  tags: ["action", "crime", "drama"],
  job_id: null,
  seasons: null,  // For movies, this is null
  status: "PUBLIC",
  trash: false,
  type: "movie",  // "movie" | "series" | "live_stream"
  content_offering_type: "PREMIUM",  // "FREE" | "PREMIUM" | "BUY_OR_RENT"
  updated_by: "user-123",
  created_by: "user-123",
  createdAt: "2024-01-01T00:00:00.000Z",
  updatedAt: "2024-01-01T00:00:00.000Z",
  category: [
    { _id: "cat-1", name: "Action" },
    { _id: "cat-2", name: "Crime" }
  ],
  genres: [
    { _id: "genre-1", name: "Action" },
    { _id: "genre-2", name: "Drama" }
  ],
  subtitles: [
    {
      title: "English",
      language: "en",
      type: "application/x-subrip",
      url: "https://example.com/subtitle.srt"
    }
  ]
};

πŸ“˜ Props

PropTypeRequiredDescription
dataISection[]YesArray of section objects containing content data and pagination metadata
isLoadingbooleanOptionalLoading state indicator (default: false)
next() => voidOptionalCallback function called when user scrolls to load more content
hasNextPagebooleanOptionalWhether more pages are available for infinite scroll
customComponentsICustomComponentsForSections[]OptionalArray of custom components to override default section renderers
hrefstringOptionalBase URL path for content links
onClick(item: IContentData) => voidOptionalCallback function called when user clicks on a content item
userSessionIWhoamiData | nullOptionalUser session data for personalized content (e.g., continue watching)

🎨 Supported Section Types

The component supports various section types defined in the type field:

normal

Standard grid layout with BasicCard

card_style_2 - card_style_6

Alternative card style variants

slider

Horizontal scrolling slider with SliderV2

slider_style_2

Alternative slider style variant

card_series_featured_style_1

Featured series card layout

new_releases

New releases section layout

top_10

Top 10 ranked content with Top10 card

continue_watching

Continue watching section (requires userSession)

🧩 Key Interfaces

The component uses interfaces from @zezosoft/zezo-ott-api-client:

// ISection structure
interface ISection {
  data: ISectionData[];  // Array of section data
  meta: {                 // Pagination metadata
    pagination: {
      page: number;
      limit: number;
      length: number;
      total: number;
      hasNextPage: boolean;
      nextPage: number | null;
    };
  };
}

// ISectionData structure
interface ISectionData {
  _id: string;
  name: string;           // Section title
  type: string;            // Section type (see supported types above)
  category: {
    _id: string;
    name: string;
    slug: string;
  };
  content: {
    data: IContentData[];  // Array of content items
    meta: {
      pagination: {
        page: number;
        limit: number;
        length: number;
        total: number;
        hasNextPage: boolean;
        nextPage: number | null;
      };
    };
  };
}

// ICustomComponentsForSections
interface ICustomComponentsForSections {
  type: string;            // Section type to override
  component: React.ReactElement; // Custom component to render
}

πŸ“¦ Required Packages

Install these packages before using Content:

npm install @zezosoft/zezo-ott-api-client react-infinite-scroll-component swiper framer-motion react-intersection-observer

# Or with pnpm
pnpm add @zezosoft/zezo-ott-api-client react-infinite-scroll-component swiper framer-motion react-intersection-observer

# Or with yarn
yarn add @zezosoft/zezo-ott-api-client react-infinite-scroll-component swiper framer-motion react-intersection-observer

⚠️ Important Notes:

  • Make sure swiper/css is imported in your global CSS or component
  • For SliderV2, ensure @zezosoft/react-player is installed if using video trailers
  • Theme colors must be configured via getThemeColors() from @/config/theme

✨ Features

♾️ Infinite Scroll

Automatically loads more sections as user scrolls down

🎨 Multiple Card Styles

Supports various card styles: normal, slider, top_10, continue_watching, etc.

πŸ”§ Custom Components

Override default section renderers with custom components

πŸ‘€ User Session Support

Personalize content based on user session (e.g., continue watching)

πŸ–±οΈ Click Handlers

Optional onClick callback for content items

⏳ Loading States

Built-in loading state support

πŸ”— Custom Links

Customizable href base path for content links

πŸ“± Responsive

Mobile-first responsive design

πŸŒ“ Dark Mode

Automatic theme support

🧩 Modular

Sections component handles individual section rendering

πŸ“ Component Structure

Main import path: @/components/zezo/Content/Content

components/zezo/Content/
β”œβ”€β”€ Content.tsx          // Main component with infinite scroll
β”œβ”€β”€ Sections.tsx         // Section renderer (handles different types)
β”œβ”€β”€ Styles/
β”‚   β”œβ”€β”€ Cards/
β”‚   β”‚   β”œβ”€β”€ BasicCard/   // Standard card layout
β”‚   β”‚   β”œβ”€β”€ Top10/       // Top 10 ranked cards
β”‚   β”‚   └── ContinueWatching/ // Continue watching cards
β”‚   └── Slider/
β”‚       └── SliderV2.tsx // Slider component
└── _components/
    β”œβ”€β”€ Loader.tsx       // Loading component
    └── Error.tsx        // Error component

🧩 All Components (34 Total)

The Content module includes 34 components organized into different categories:

1️⃣ Main Components (2)

Content.tsx

Main component with infinite scroll wrapper

Sections.tsx

Section renderer that handles different section types

2️⃣ Card Components (4)

BasicCard.tsx

Standard horizontal scrolling card layout with Swiper, hover effects, and play/favorite buttons

Top10.tsx

Top 10 ranked content with numbered badges (1-10)

ContinueWatching.tsx

Continue watching section with progress bars and episode info (requires userSession)

InteractiveCard.tsx

Interactive card with scale animations on hover and expanded details

3️⃣ Slider Components (3)

See live previews at Slider Preview

Slider.tsx

Basic slider with framer-motion animations and auto-play

SliderV1.tsx

Hero slider with thumbnail strip, promo text, and MainHomeTabAndMobileSlider

SliderV2.tsx

Advanced slider with trailer auto-play, mute controls, and video player integration

4️⃣ ContentDetails Component (19)

Main content details page component with all subcomponents:

ContentDetails.tsx

Main content details component with video player integration

Subcomponents:

  • DetailsOverlayV2.tsx - Content overlay with banners, buttons, and metadata
  • TabSwitcher.tsx - Tab navigation component (Episodes, Related, Details)
  • DetailsTab.tsx - Content details tab with cast, genres, languages
  • EpisodesTab.tsx - Episodes list tab for series content
  • RelatedTab.tsx - Related content recommendations tab
  • PlayButton.tsx - Play button component
  • TrailerButton.tsx - Trailer play button
  • ShareButton.tsx - Share content button
  • ShareModal.tsx - Share modal dialog
  • favoriteButton.tsx - Favorite/Bookmark button
  • SeasonSelectorDropdown.tsx - Season selector dropdown for series
  • ReadMoreText.tsx - Expandable read more text component
  • RecContent.tsx - Recommended content component
  • RentOrBuyIcon.tsx - Rent/Buy icon indicator
  • RentTypeWithInfo.tsx - Rent type information display
  • GradientOverlay.tsx - Gradient overlay component
  • Dropdown.tsx - Dropdown component
  • VideoPlayerProvider.tsx - Video player context provider

5️⃣ Skeleton/Loading Components (4)

SkeletonLoader.tsx

Main skeleton loader component

BasicCardSkeleton.tsx

Skeleton for card layouts

SliderSkeleton.tsx

Skeleton for slider layouts

LazyImage.tsx

Lazy loading image component with intersection observer

6️⃣ Helper Components (2)

Loader.tsx

Loading spinner component

Error.tsx

Error display component

7️⃣ Hooks (1)

buildPlaylist.ts

Hook to build video player playlist from content data

πŸ’» Component Usage Examples

ContentDetails Component

import ContentDetails from "@/components/zezo/Content/_components/ContentDetails/ContentDetails";
import type { IContentData, IWhoamiData, IContentSeasons } from "@zezosoft/zezo-ott-api-client";

<ContentDetails
  content={contentData}                    // IContentData
  userSession={userSession}                // IWhoamiData | null
  seasons={seasonsData}                    // IContentSeasons[] | null
  seasonsData={seasonsData}                // IContentSeasons[] | null
  isBuyOrRent={false}                      // boolean
  isTabSwitcher={true}                     // boolean
  buttons={[
    {
      type: "play",
      label: "Play",
      icon: <PlayIcon />,
      onClick: ({ value }) => handlePlay(value)
    },
    {
      type: "favorite",
      label: "Add to Favorites",
      icon: <HeartIcon />,
      onClick: ({ value }) => handleFavorite(value),
      isLoadingFavorite: false
    }
  ]}
  moreData={{
    relatedContent: relatedSection,
    moreDetails: {
      description: contentData.description,
      cast: contentData.cast,
      genres: contentData.genres
    },
    episodeTab: {
      onClick: (episode) => handleEpisodeClick(episode)
    }
  }}
/>

BasicCard Component

import BasicCard from "@/components/zezo/Content/Styles/Cards/BasicCard/BasicCard";

<BasicCard
  title="Trending Now"
  data={contentArray}        // IContentData[]
  href="/details"            // Optional: base URL
  userSession={userSession}  // Optional: IWhoami | null
/>

Slider Component

import Slider from "@/components/zezo/Content/Styles/Slider/Slider";

<Slider
  data={contentArray}        // IContentData[]
  onWatchNow={(item) => {   // Optional callback
    console.log("Watch:", item.name);
  }}
/>

SliderV1 Component

import SliderV1 from "@/components/zezo/Content/Styles/Slider/SliderV1";

<SliderV1
  data={contentArray}        // IContentData[]
  forceDesktopForPreview     // Optional: true to always show desktop layout
/>

SliderV2 Component

import SliderV2 from "@/components/zezo/Content/Styles/Slider/SliderV2";

<SliderV2
  data={featuredContent}     // IContentData[]
  onWatchNow={(item) => {   // Optional callback
    console.log("Watch:", item.name);
  }}
/>

πŸ”§ Common Issues & Solutions

❌ Issue: Sections not rendering

Cause: Data structure doesn't match expected format

Solution: Ensure your data follows the ISection interface exactly. Check that:

  • data is an array of ISectionData objects
  • Each section has content.data array with IContentData[]
  • Pagination metadata is correctly structured

❌ Issue: Infinite scroll not working

Cause: Missing next callback or incorrect hasNextPage

Solution:

  • Provide the next callback function
  • Set hasNextPage to true when more data is available
  • Update hasNextPage to false after loading all pages

❌ Issue: Continue watching not showing

Cause: Missing userSession prop

Solution: Pass the userSession prop with user data containing watch history sessions

❌ Issue: Cards not scrolling horizontally

Cause: Swiper CSS not imported or missing dependencies

Solution:

// Add this to your global CSS or component file
import "swiper/css";
import "swiper/css/navigation";
import "swiper/css/free-mode";

❌ Issue: Type errors with IContentData

Cause: Type mismatch or missing type definitions

Solution: Ensure you're importing types from the correct package:

import type {
  IContentData,
  ISection,
  ISectionData,
  IWhoamiData,
  IContentSeasons
} from "@zezosoft/zezo-ott-api-client";

✨ Best Practices

βœ… Performance Tips

  • Use React.memo() for custom components passed via customComponents
  • Implement proper pagination to avoid loading too much data at once
  • Use LazyImage component for better image loading performance
  • Consider using virtual scrolling for large datasets

πŸ’‘ Code Organization

  • Create a separate data fetching hook (e.g., useContentSections)
  • Handle loading and error states properly
  • Use TypeScript for type safety
  • Group custom components by section type for better maintainability

🎨 Customization Tips

  • Override default section renderers using customComponents prop
  • Customize card styles by importing individual card components
  • Use theme colors via getThemeColors() for consistent styling
  • Modify href prop to customize navigation paths

πŸ“‚ Import Paths Reference

Quick reference for importing all Content module components:

// Main Components
import Content from "@/components/zezo/Content/Content";
import Sections from "@/components/zezo/Content/Sections";

// Card Components
import BasicCard from "@/components/zezo/Content/Styles/Cards/BasicCard/BasicCard";
import Top10 from "@/components/zezo/Content/Styles/Cards/Top10/Top10";
import ContinueWatching from "@/components/zezo/Content/Styles/Cards/ContinueWatching/ContinueWatching";
import InteractiveCard from "@/components/zezo/Content/Styles/Cards/BasicCard/InteractiveCard";

// Slider Components
import Slider from "@/components/zezo/Content/Styles/Slider/Slider";
import SliderV1 from "@/components/zezo/Content/Styles/Slider/SliderV1";
import SliderV2 from "@/components/zezo/Content/Styles/Slider/SliderV2";

// ContentDetails
import ContentDetails from "@/components/zezo/Content/_components/ContentDetails/ContentDetails";

// Subcomponents (if needed individually)
import DetailsOverlayV2 from "@/components/zezo/Content/_components/ContentDetails/_components/DetailsOverlayV2";
import TabSwitcher from "@/components/zezo/Content/_components/ContentDetails/_components/Tabs/TabSwitcher";
import PlayButton from "@/components/zezo/Content/_components/ContentDetails/_components/PlayButton";
// ... and more

// Skeleton Components
import SkeletonLoader from "@/components/zezo/Content/Skeleton-components/SkeletonLoader";
import LazyImage from "@/components/zezo/Content/Skeleton-components/LazyImage";

// Helper Components
import Loader from "@/components/zezo/Content/_components/Loader";
import Error from "@/components/zezo/Content/_components/Error";

// Hooks
import { buildPlaylist } from "@/components/zezo/Content/hooks/buildPlaylist";

πŸ“š Additional Resources

πŸ”— API Client Documentation

Refer to @zezosoft/zezo-ott-api-client package documentation for complete interface definitions

🎨 Theming

Configure dynamic theme colors via NEXT_PUBLIC_THEME_COLORS environment variable

πŸ“± Responsive Design

All components are mobile-first and responsive. Cards adapt to screen size automatically

πŸŒ“ Dark Mode

Automatic dark mode support via Tailwind CSS classes. No additional configuration needed