We use cookies to enhance your experience on the site
CodeWorlds

React Components - Building UI

Now we'll create React components for Kraken's Call. You'll learn to create reusable UI elements.

What is a Component?

A component is a function that returns JSX (UI appearance). It's like LEGO blocks - you build small pieces and combine them into a bigger whole.

Simple component:

1function WelcomeMessage() {
2  return (
3    <div className="bg-blue-100 p-4 rounded">
4      <h2 className="text-xl font-bold">Welcome aboard!</h2>
5      <p>Ready to send messages?</p>
6    </div>
7  );
8}

Using a component:

1export default function Home() {
2  return (
3    <div>
4      <WelcomeMessage />
5      <WelcomeMessage />
6      <WelcomeMessage />
7    </div>
8  );
9}

Props - Passing Data

Props are data you pass to a component. Like function arguments.

Component with props:

1function PirateCard({ name, role, avatar }: {
2  name: string;
3  role: string;
4  avatar: string;
5}) {
6  return (
7    <div className="bg-white p-6 rounded-lg shadow">
8      <div className="text-4xl mb-2">{avatar}</div>
9      <h3 className="text-xl font-bold">{name}</h3>
10      <p className="text-gray-600">{role}</p>
11    </div>
12  );
13}

Using with props:

1export default function Crew() {
2  return (
3    <div className="grid grid-cols-3 gap-4">
4      <PirateCard
5        name="Captain Redbeard"
6        role="Ship captain"
7        avatar="🏴‍☠️"
8      />
9      <PirateCard
10        name="Morgan Blackheart"
11        role="Navigator"
12        avatar="🗺️"
13      />
14      <PirateCard
15        name="Polly Squawk"
16        role="Communication parrot"
17        avatar="🦜"
18      />
19    </div>
20  );
21}

TypeScript Props - Types for Safety

It's better to use an interface for props:

1interface PirateCardProps {
2  name: string;
3  role: string;
4  avatar: string;
5  description?: string; // Optional field
6}
7
8function PirateCard({ name, role, avatar, description }: PirateCardProps) {
9  return (
10    <div className="bg-white p-6 rounded-lg shadow">
11      <div className="text-4xl mb-2">{avatar}</div>
12      <h3 className="text-xl font-bold">{name}</h3>
13      <p className="text-gray-600">{role}</p>
14      {description && (
15        <p className="text-sm text-gray-500 mt-2">{description}</p>
16      )}
17    </div>
18  );
19}

Post Component - Twitter-style post

Let's create a post component for Kraken's Call:

1// components/Post.tsx
2interface PostProps {
3  author: string;
4  avatar: string;
5  content: string;
6  timestamp: string;
7  likes: number;
8}
9
10export default function Post({
11  author,
12  avatar,
13  content,
14  timestamp,
15  likes
16}: PostProps) {
17  return (
18    <div className="bg-white p-6 rounded-lg shadow hover:shadow-lg transition-shadow">
19      {/* Header */}
20      <div className="flex items-center gap-3 mb-4">
21        <div className="text-3xl">{avatar}</div>
22        <div>
23          <h3 className="font-bold text-gray-900">{author}</h3>
24          <p className="text-sm text-gray-500">{timestamp}</p>
25        </div>
26      </div>
27
28      {/* Content */}
29      <p className="text-gray-800 mb-4">{content}</p>
30
31      {/* Actions */}
32      <div className="flex items-center gap-6 text-gray-500">
33        <button className="flex items-center gap-2 hover:text-red-500">
34          ❤️ {likes}
35        </button>
36        <button className="flex items-center gap-2 hover:text-blue-500">
37          💬 Reply
38        </button>
39        <button className="flex items-center gap-2 hover:text-green-500">
40          🔄 Share
41        </button>
42      </div>
43    </div>
44  );
45}

Using the Post component:

1// app/page.tsx
2import Post from '@/components/Post';
3
4export default function Feed() {
5  return (
6    <div className="max-w-2xl mx-auto p-8 space-y-4">
7      <Post
8        author="Captain Redbeard"
9        avatar="🏴‍☠️"
10        content="We just discovered a new island! The map points to a huge treasure 🗺️💰"
11        timestamp="2 hours ago"
12        likes={42}
13      />
14      <Post
15        author="Morgan Blackheart"
16        avatar="⚓"
17        content="Who wants to join the expedition to the north? Looking for brave sailors!"
18        timestamp="5 hours ago"
19        likes={18}
20      />
21    </div>
22  );
23}

Component Organization

Standard Next.js project structure:

1app/
2├── page.tsx              → Pages
3├── layout.tsx
4└── profile/
5    └── page.tsx
6
7components/                → Reusable components
8├── Post.tsx
9├── Navbar.tsx
10├── Button.tsx
11└── Avatar.tsx
12
13lib/                       → Utility functions
14└── utils.ts
15
16types/                     → TypeScript types
17└── index.ts

Client vs Server Components

In Next.js 14 everything is a Server Component by default.

Server Component (default):

  • Rendered on the server
  • No access to browser API (useState, useEffect)
  • Faster, lighter
  • Can fetch data directly
1// Server Component (default)
2export default function ServerPost() {
3  // Can fetch data here
4  return <div>Post</div>;
5}

Client Component:

  • Rendered in the browser
  • Can use hooks (useState, useEffect)
  • Interactive (onClick, onChange)
  • Requires
    'use client'
    at the top
1'use client';
2
3import { useState } from 'react';
4
5export default function LikeButton() {
6  const [likes, setLikes] = useState(0);
7
8  return (
9    <button onClick={() => setLikes(likes + 1)}>
10      ❤️ {likes}
11    </button>
12  );
13}

Rule: Use Server Components by default. Add

'use client'
only when you need interactivity.

Interactive Post with Like Button

1'use client';
2
3import { useState } from 'react';
4
5interface PostProps {
6  author: string;
7  avatar: string;
8  content: string;
9  initialLikes: number;
10}
11
12export default function InteractivePost({
13  author,
14  avatar,
15  content,
16  initialLikes
17}: PostProps) {
18  const [likes, setLikes] = useState(initialLikes);
19  const [isLiked, setIsLiked] = useState(false);
20
21  const handleLike = () => {
22    if (isLiked) {
23      setLikes(likes - 1);
24    } else {
25      setLikes(likes + 1);
26    }
27    setIsLiked(!isLiked);
28  };
29
30  return (
31    <div className="bg-white p-6 rounded-lg shadow">
32      <div className="flex items-center gap-3 mb-4">
33        <div className="text-3xl">{avatar}</div>
34        <h3 className="font-bold">{author}</h3>
35      </div>
36
37      <p className="mb-4">{content}</p>
38
39      <button
40        onClick={handleLike}
41        className={`flex items-center gap-2 ${
42          isLiked ? 'text-red-500' : 'text-gray-500'
43        }`}
44      >
45        {isLiked ? '❤️' : '🤍'} {likes}
46      </button>
47    </div>
48  );
49}

Children Prop - Wrapper Components

children
is a special prop - the content between component tags.

1interface CardProps {
2  children: React.ReactNode;
3}
4
5function Card({ children }: CardProps) {
6  return (
7    <div className="bg-white p-6 rounded-lg shadow">
8      {children}
9    </div>
10  );
11}
12
13// Usage:
14<Card>
15  <h2>Title</h2>
16  <p>Card content</p>
17</Card>

Conditional Rendering

Showing elements conditionally:

1function Post({ isVerified, isPremium }: {
2  isVerified: boolean;
3  isPremium: boolean;
4}) {
5  return (
6    <div>
7      <h3>Captain Redbeard</h3>
8
9      {/* If statement */}
10      {isVerified && <span>Verified</span>}
11
12      {/* Ternary operator */}
13      {isPremium ? (
14        <span className="text-gold">Premium</span>
15      ) : (
16        <span className="text-gray-500">Free</span>
17      )}
18    </div>
19  );
20}

Mapping Lists - Rendering Lists

Rendering an array of data:

1const pirates = [
2  { id: 1, name: 'Redbeard', role: 'Captain' },
3  { id: 2, name: 'Blackheart', role: 'Navigator' },
4  { id: 3, name: 'Polly', role: 'Parrot' },
5];
6
7function PirateList() {
8  return (
9    <div className="space-y-4">
10      {pirates.map((pirate) => (
11        <div key={pirate.id} className="bg-white p-4 rounded">
12          <h3 className="font-bold">{pirate.name}</h3>
13          <p className="text-gray-600">{pirate.role}</p>
14        </div>
15      ))}
16    </div>
17  );
18}

Important: Always add the

key
prop when mapping!

Summary 🎓

Components - functions returning JSX ✅ Props - passing data to components ✅ TypeScript interface - types for props ✅ Server Components - default, fast ✅ Client Components -

'use client'
, interactive ✅ useState - component state ✅ children - content between tags ✅ Conditional rendering - && and ternary operator ✅ map() - rendering lists with key prop

In the next exercise we'll build a complete feed with the ability to add posts!

See you there! 🚀

Go to CodeWorlds