We use cookies to enhance your experience on the site
CodeWorlds

Playlists and Collections - Organizing Videos

YouTube lets you organize videos into playlists. Pirates will group their adventures into collections!

Playlist Structure

1interface Playlist {
2  id: string;
3  title: string;
4  description: string;
5  thumbnail: string; // Thumbnail of the first video
6  creator: {
7    id: string;
8    name: string;
9    avatar: string;
10  };
11  videos: Video[];
12  isPublic: boolean;
13  createdDate: Date;
14  updatedDate: Date;
15}

Playlist Card

1function PlaylistCard({ playlist }: { playlist: Playlist }) {
2  return (
3    <div className="group cursor-pointer">
4      {/* Playlist Thumbnail Stack */}
5      <div className="relative aspect-video bg-gray-900 rounded-lg overflow-hidden mb-2">
6        {/* Main Thumbnail */}
7        <img
8          src={playlist.thumbnail}
9          alt={playlist.title}
10          className="w-full h-full object-cover"
11        />
12
13        {/* Playlist Overlay */}
14        <div className="absolute inset-0 bg-gradient-to-r from-transparent to-black/80 flex items-center justify-end p-4">
15          <div className="text-white text-right">
16            <div className="text-4xl mb-2">📋</div>
17            <p className="font-bold">{playlist.videos.length}</p>
18            <p className="text-xs">videos</p>
19          </div>
20        </div>
21
22        {/* Play All Button */}
23        <div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity flex items-center justify-center">
24          <button className="opacity-0 group-hover:opacity-100 transition-opacity bg-white text-black px-6 py-3 rounded-full font-bold flex items-center gap-2">
25Play all
26          </button>
27        </div>
28      </div>
29
30      {/* Playlist Info */}
31      <h3 className="font-semibold line-clamp-2 mb-1">{playlist.title}</h3>
32      <p className="text-sm text-gray-600">
33        {playlist.creator.name}
34      </p>
35      <p className="text-sm text-gray-600">
36        {playlist.isPublic ? 'Public' : 'Private'}{playlist.videos.length} videos
37      </p>
38    </div>
39  );
40}

Playlist Page - Playlist Details

1'use client';
2
3import { useState } from 'react';
4
5export default function PlaylistPage({ playlist }: { playlist: Playlist }) {
6  const [currentVideoIndex, setCurrentVideoIndex] = useState(0);
7  const currentVideo = playlist.videos[currentVideoIndex];
8
9  return (
10    <div className="max-w-7xl mx-auto p-4">
11      <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
12        {/* Left - Video Player */}
13        <div className="lg:col-span-2">
14          <VideoPlayer video={currentVideo} />
15
16          <div className="mt-4 bg-gradient-to-r from-purple-600 to-blue-600 text-white p-4 rounded-lg">
17            <div className="flex items-center gap-3 mb-2">
18              <span className="text-2xl">📋</span>
19              <div>
20                <h2 className="font-bold text-lg">{playlist.title}</h2>
21                <p className="text-sm opacity-90">
22                  {currentVideoIndex + 1} / {playlist.videos.length}
23                </p>
24              </div>
25            </div>
26            <p className="text-sm opacity-80">{playlist.description}</p>
27          </div>
28        </div>
29
30        {/* Right - Playlist Videos */}
31        <div className="bg-white rounded-lg shadow">
32          <div className="p-4 border-b">
33            <h3 className="font-bold">Playlist</h3>
34            <p className="text-sm text-gray-600">
35              {playlist.videos.length} videos
36            </p>
37          </div>
38
39          <div className="max-h-[600px] overflow-y-auto">
40            {playlist.videos.map((video, index) => (
41              <div
42                key={video.id}
43                onClick={() => setCurrentVideoIndex(index)}
44                className={`flex gap-3 p-3 cursor-pointer border-b hover:bg-gray-50 ${
45                  index === currentVideoIndex ? 'bg-blue-50' : ''
46                }`}
47              >
48                {/* Index */}
49                <div className="flex-shrink-0 w-6 text-center text-gray-600 font-medium">
50                  {index === currentVideoIndex ? '▶' : index + 1}
51                </div>
52
53                {/* Thumbnail */}
54                <div className="relative w-32 aspect-video bg-gray-200 rounded flex-shrink-0">
55                  <img
56                    src={video.thumbnail}
57                    alt={video.title}
58                    className="w-full h-full object-cover"
59                  />
60                  <div className="absolute bottom-1 right-1 bg-black bg-opacity-80 text-white text-xs px-1 rounded">
61                    {formatTime(video.duration)}
62                  </div>
63                </div>
64
65                {/* Info */}
66                <div className="flex-1 min-w-0">
67                  <h4 className="text-sm font-semibold line-clamp-2 mb-1">
68                    {video.title}
69                  </h4>
70                  <p className="text-xs text-gray-600">
71                    {video.channel.name}
72                  </p>
73                </div>
74              </div>
75            ))}
76          </div>
77        </div>
78      </div>
79    </div>
80  );
81}

Create Playlist - Creating a Playlist

1'use client';
2
3import { useState } from 'react';
4
5export default function CreatePlaylist({ onSave }: { onSave: (playlist: Playlist) => void }) {
6  const [title, setTitle] = useState('');
7  const [description, setDescription] = useState('');
8  const [isPublic, setIsPublic] = useState(true);
9
10  const handleSubmit = (e: React.FormEvent) => {
11    e.preventDefault();
12
13    const newPlaylist: Playlist = {
14      id: Date.now().toString(),
15      title,
16      description,
17      thumbnail: '/default-playlist.jpg',
18      creator: {
19        id: 'current-user',
20        name: 'Captain Redbeard',
21        avatar: '/avatar.jpg',
22      },
23      videos: [],
24      isPublic,
25      createdDate: new Date(),
26      updatedDate: new Date(),
27    };
28
29    onSave(newPlaylist);
30  };
31
32  return (
33    <div className="bg-white rounded-lg shadow p-6">
34      <h2 className="text-xl font-bold mb-4">Create New Playlist</h2>
35
36      <form onSubmit={handleSubmit} className="space-y-4">
37        <div>
38          <label className="block font-medium mb-2">Title</label>
39          <input
40            type="text"
41            value={title}
42            onChange={(e) => setTitle(e.target.value)}
43            className="w-full p-3 border rounded-lg"
44            required
45          />
46        </div>
47
48        <div>
49          <label className="block font-medium mb-2">Description</label>
50          <textarea
51            value={description}
52            onChange={(e) => setDescription(e.target.value)}
53            className="w-full p-3 border rounded-lg resize-none"
54            rows={4}
55          />
56        </div>
57
58        <div>
59          <label className="flex items-center gap-2 cursor-pointer">
60            <input
61              type="checkbox"
62              checked={isPublic}
63              onChange={(e) => setIsPublic(e.target.checked)}
64              className="w-4 h-4"
65            />
66            <span className="font-medium">Public</span>
67          </label>
68          <p className="text-sm text-gray-600 ml-6">
69            {isPublic
70              ? 'Anyone can view this playlist'
71              : 'Only you can view this playlist'}
72          </p>
73        </div>
74
75        <button
76          type="submit"
77          className="w-full py-3 bg-red-600 text-white rounded-lg font-bold hover:bg-red-700 transition"
78        >
79          Create Playlist
80        </button>
81      </form>
82    </div>
83  );
84}

Add to Playlist - Adding a Video to a Playlist

1'use client';
2
3import { useState } from 'react';
4
5interface AddToPlaylistProps {
6  video: Video;
7  playlists: Playlist[];
8  onAdd: (playlistId: string, video: Video) => void;
9}
10
11export default function AddToPlaylist({ video, playlists, onAdd }: AddToPlaylistProps) {
12  const [isOpen, setIsOpen] = useState(false);
13
14  return (
15    <>
16      <button
17        onClick={() => setIsOpen(true)}
18        className="flex items-center gap-2 px-4 py-2 bg-gray-100 rounded-full hover:bg-gray-200"
19      >
20        📋 Save
21      </button>
22
23      {isOpen && (
24        <div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
25          <div className="bg-white rounded-lg max-w-md w-full">
26            <div className="flex items-center justify-between p-4 border-b">
27              <h3 className="font-bold">Save to playlist</h3>
28              <button
29                onClick={() => setIsOpen(false)}
30                className="text-gray-500 hover:text-gray-700 text-2xl"
31              >
3233              </button>
34            </div>
35
36            <div className="p-4 max-h-96 overflow-y-auto">
37              {playlists.length === 0 ? (
38                <p className="text-gray-600 text-center py-8">
39                  No playlists yet. Create one!
40                </p>
41              ) : (
42                <div className="space-y-2">
43                  {playlists.map(playlist => {
44                    const isInPlaylist = playlist.videos.some(v => v.id === video.id);
45
46                    return (
47                      <label
48                        key={playlist.id}
49                        className="flex items-center gap-3 p-3 hover:bg-gray-50 rounded cursor-pointer"
50                      >
51                        <input
52                          type="checkbox"
53                          checked={isInPlaylist}
54                          onChange={() => {
55                            if (!isInPlaylist) {
56                              onAdd(playlist.id, video);
57                            }
58                          }}
59                          className="w-4 h-4"
60                        />
61                        <div className="flex-1">
62                          <h4 className="font-medium">{playlist.title}</h4>
63                          <p className="text-sm text-gray-600">
64                            {playlist.videos.length} videos • {playlist.isPublic ? 'Public' : 'Private'}
65                          </p>
66                        </div>
67                      </label>
68                    );
69                  })}
70                </div>
71              )}
72            </div>
73
74            <div className="p-4 border-t">
75              <button className="w-full py-2 bg-blue-600 text-white rounded-lg font-bold hover:bg-blue-700">
76                + Create new playlist
77              </button>
78            </div>
79          </div>
80        </div>
81      )}
82    </>
83  );
84}

Summary 🎓

Playlist structure - title, description, videos array ✅ Playlist card - thumbnail stack, video count ✅ Playlist page - player + video list ✅ Current video tracking - index state ✅ Create playlist - form with public/private toggle ✅ Add to playlist - modal with checkboxes ✅ Auto-play next - sequential playback ✅ Playlist management - add/remove videos

In the next exercise we'll build Channel pages and subscriptions!

See you there! 🚀

Go to CodeWorlds