Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Redirects Navigation Guards
Your Progress0%
0 of 70 completed

NextJS Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication

Redirects and Navigation Guards

Protecting routes and implementing secure navigation flows

Not all pages should be accessible to everyone. Some routes require authentication, others need specific roles or permissions. Redirects send users to different pages based on conditions, while navigation guards protect routes from unauthorized access. Whether you're building login flows, protecting admin panels, or handling permissions, Next.js provides powerful tools for secure navigation. Let's master redirects and route protection!

Redirect Basics

Server Component Redirects

Use redirect() in Server Components and Server Actions:

app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

async function getUser() {
  const sessionCookie = cookies().get('session');
  if (!sessionCookie) return null;
  
  // Verify session and get user
  const user = await verifySession(sessionCookie.value);
  return user;
}

export default async function DashboardPage() {
  const user = await getUser();

  // Redirect if not authenticated
  if (!user) {
    redirect('/login');
  }

  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      <p>This is your dashboard.</p>
    </div>
  );
}

// ✅ Server-side redirect
// ✅ Runs before page renders
// ✅ SEO-friendly
// ✅ No flash of protected content

Client Component Redirects

Use useRouter in Client Components:

components/LoginForm.tsx
'use client';

import { useRouter } from 'next/navigation';
import { useState } from 'react';

export function LoginForm() {
  const router = useRouter();
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    setLoading(true);

    const formData = new FormData(e.currentTarget);
    const email = formData.get('email') as string;
    const password = formData.get('password') as string;

    try {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });

      if (response.ok) {
        // Redirect to dashboard after successful login
        router.replace('/dashboard');
      } else {
        alert('Login failed');
      }
    } catch (error) {
      alert('An error occurred');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <input
        type="email"
        name="email"
        placeholder="Email"
        required
        className="w-full px-4 py-2 border rounded-lg"
      />
      <input
        type="password"
        name="password"
        placeholder="Password"
        required
        className="w-full px-4 py-2 border rounded-lg"
      />
      <button
        type="submit"
        disabled={loading}
        className="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400"
      >
        {loading ? 'Logging in...' : 'Login'}
      </button>
    </form>
  );
}

// ✅ Client-side redirect after login
// ✅ Use router.replace() so user can't go back to login
// ✅ Handle loading state

Permanent Redirects

app/old-blog/page.tsx
import { permanentRedirect } from 'next/navigation';

export default function OldBlogPage() {
  // Content moved permanently to new location
  permanentRedirect('/blog');
}

// Use permanentRedirect() for:
// ✅ Moved content that won't change back
// ✅ Old URLs that should be updated in search engines
// ✅ Deprecated routes

// Returns 308 status code (permanent redirect)
// Search engines update their indexes
// Browsers cache the redirect

// Use redirect() for:
// ✅ Temporary redirects
// ✅ Authentication redirects
// ✅ Conditional redirects

// Returns 307 status code (temporary redirect)
// Search engines don't update indexes

Middleware for Route Protection

Middleware runs before requests reach your pages, making it ideal for authentication:

Basic Authentication Middleware

middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // Get session token from cookie
  const token = request.cookies.get('session')?.value;

  // Check if accessing protected route
  const isProtectedRoute = request.nextUrl.pathname.startsWith('/dashboard');

  if (isProtectedRoute && !token) {
    // Redirect to login if not authenticated
    const loginUrl = new URL('/login', request.url);
    loginUrl.searchParams.set('redirect', request.nextUrl.pathname);
    return NextResponse.redirect(loginUrl);
  }

  return NextResponse.next();
}

// Specify which routes to run middleware on
export const config = {
  matcher: [
    '/dashboard/:path*',
    '/admin/:path*',
    '/profile/:path*',
  ],
};

// ✅ Runs before page loads
// ✅ Protects multiple routes at once
// ✅ Adds redirect parameter for return after login
// ✅ Efficient - runs on Edge Runtime

Advanced Middleware with Role Checks

middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifyToken } from '@/lib/auth';

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('session')?.value;
  const { pathname } = request.nextUrl;

  // Public routes - allow without authentication
  const publicRoutes = ['/', '/login', '/signup', '/about'];
  if (publicRoutes.includes(pathname)) {
    return NextResponse.next();
  }

  // Check authentication
  if (!token) {
    const loginUrl = new URL('/login', request.url);
    loginUrl.searchParams.set('redirect', pathname);
    return NextResponse.redirect(loginUrl);
  }

  // Verify token and get user info
  const user = await verifyToken(token);
  if (!user) {
    // Invalid token - clear cookie and redirect
    const response = NextResponse.redirect(new URL('/login', request.url));
    response.cookies.delete('session');
    return response;
  }

  // Admin-only routes
  if (pathname.startsWith('/admin')) {
    if (user.role !== 'admin') {
      // Not authorized - redirect to dashboard
      return NextResponse.redirect(new URL('/dashboard', request.url));
    }
  }

  // Add user info to request headers (accessible in Server Components)
  const response = NextResponse.next();
  response.headers.set('x-user-id', user.id);
  response.headers.set('x-user-role', user.role);

  return response;
}

export const config = {
  matcher: [
    // Protected routes
    '/dashboard/:path*',
    '/admin/:path*',
    '/profile/:path*',
    '/settings/:path*',
  ],
};

// ✅ Authentication check
// ✅ Role-based access control
// ✅ Invalid token handling
// ✅ User info in headers
// ✅ Granular route protection

Authentication Patterns

Pattern 1: Protected Page with Auth Check

app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

async function getSession() {
  const sessionCookie = cookies().get('session');
  if (!sessionCookie) return null;

  try {
    const session = await verifySession(sessionCookie.value);
    return session;
  } catch {
    return null;
  }
}

export default async function DashboardPage() {
  const session = await getSession();

  if (!session) {
    redirect('/login');
  }

  // Fetch user-specific data
  const userData = await fetch(
    `https://api.example.com/users/${session.userId}`,
    { cache: 'no-store' }
  ).then(r => r.json());

  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-8">
        Welcome, {userData.name}!
      </h1>

      <div className="grid grid-cols-3 gap-6">
        <StatCard title="Orders" value={userData.orderCount} />
        <StatCard title="Points" value={userData.points} />
        <StatCard title="Level" value={userData.level} />
      </div>
    </div>
  );
}

// ✅ Server-side authentication check
// ✅ Redirect before rendering
// ✅ Fetch user-specific data after auth
// ✅ No protected content leaks

Pattern 2: Login with Redirect Back

app/login/page.tsx
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';
import LoginForm from './LoginForm';

async function getSession() {
  const sessionCookie = cookies().get('session');
  if (!sessionCookie) return null;
  return await verifySession(sessionCookie.value);
}

export default async function LoginPage({
  searchParams,
}: {
  searchParams: { redirect?: string };
}) {
  // If already logged in, redirect
  const session = await getSession();
  if (session) {
    redirect(searchParams.redirect || '/dashboard');
  }

  return (
    <div className="min-h-screen flex items-center justify-center">
      <div className="max-w-md w-full">
        <h1 className="text-3xl font-bold mb-8 text-center">
          Login
        </h1>
        <LoginForm redirectTo={searchParams.redirect} />
      </div>
    </div>
  );
}

// ✅ Redirect if already authenticated
// ✅ Preserve redirect parameter
// ✅ Return to intended page after login
app/login/LoginForm.tsx
'use client';

import { useRouter } from 'next/navigation';

export default function LoginForm({ 
  redirectTo 
}: { 
  redirectTo?: string 
}) {
  const router = useRouter();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    
    // Login logic...
    const success = await login(email, password);

    if (success) {
      // Redirect to intended page or dashboard
      router.replace(redirectTo || '/dashboard');
    }
  };

  return <form onSubmit={handleSubmit}>{/* form fields */}</form>;
}

// Flow:
// 1. User tries to access /dashboard
// 2. Middleware redirects to /login?redirect=/dashboard
// 3. User logs in
// 4. Redirects back to /dashboard

// ✅ Preserves user's intended destination
// ✅ Better user experience
// ✅ Works with bookmarked protected URLs

Pattern 3: Role-Based Access

app/admin/page.tsx
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

async function getUser() {
  const sessionCookie = cookies().get('session');
  if (!sessionCookie) return null;

  const session = await verifySession(sessionCookie.value);
  if (!session) return null;

  const user = await getUserById(session.userId);
  return user;
}

export default async function AdminPage() {
  const user = await getUser();

  // Check authentication
  if (!user) {
    redirect('/login');
  }

  // Check authorization
  if (user.role !== 'admin') {
    redirect('/dashboard'); // Or show 403 error
  }

  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-8">Admin Panel</h1>
      
      <div className="bg-yellow-50 border border-yellow-200 p-4 rounded-lg mb-6">
        <p className="text-sm text-yellow-800">
          ⚠️ You're in the admin panel. Be careful!
        </p>
      </div>

      <AdminDashboard />
    </div>
  );
}

// ✅ Two-step check: authentication + authorization
// ✅ Clear separation of concerns
// ✅ Redirects non-admins to safe page
// ✅ Visual warning for admins

Redirects in Server Actions

Form Submission with Redirect

app/actions/posts.ts
'use server';

import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';
import { revalidatePath } from 'next/cache';

export async function createPost(formData: FormData) {
  // Check authentication
  const session = cookies().get('session');
  if (!session) {
    redirect('/login');
  }

  // Extract form data
  const title = formData.get('title') as string;
  const content = formData.get('content') as string;

  // Validate
  if (!title || !content) {
    throw new Error('Title and content are required');
  }

  // Create post
  const post = await db.posts.create({
    data: {
      title,
      content,
      authorId: session.value,
    },
  });

  // Revalidate blog page
  revalidatePath('/blog');

  // Redirect to the new post
  redirect(`/blog/${post.slug}`);
}

// ✅ Server Action with redirect
// ✅ Authentication check
// ✅ Revalidate cache
// ✅ Redirect to created resource

Multi-Step Form with Conditional Redirects

app/actions/onboarding.ts
'use server';

import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

export async function saveOnboardingStep(
  step: number,
  formData: FormData
) {
  const session = cookies().get('session');
  if (!session) {
    redirect('/login');
  }

  // Save step data
  await saveStepData(session.value, step, formData);

  // Determine next step
  if (step === 1) {
    redirect('/onboarding/step-2');
  } else if (step === 2) {
    redirect('/onboarding/step-3');
  } else if (step === 3) {
    // Onboarding complete
    await markOnboardingComplete(session.value);
    redirect('/dashboard');
  }
}

// ✅ Handles multi-step flow
// ✅ Conditional redirects based on step
// ✅ Completes onboarding on final step

Complete Authentication Examples

Example 1: Complete Auth Guard Utility

lib/auth-guards.ts
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

interface User {
  id: string;
  email: string;
  name: string;
  role: 'user' | 'admin';
}

export async function requireAuth(): Promise<User> {
  const sessionCookie = cookies().get('session');

  if (!sessionCookie) {
    redirect('/login');
  }

  try {
    const user = await verifySession(sessionCookie.value);
    if (!user) {
      redirect('/login');
    }
    return user;
  } catch {
    redirect('/login');
  }
}

export async function requireAdmin(): Promise<User> {
  const user = await requireAuth();

  if (user.role !== 'admin') {
    redirect('/dashboard');
  }

  return user;
}

export async function requireRole(
  allowedRoles: string[]
): Promise<User> {
  const user = await requireAuth();

  if (!allowedRoles.includes(user.role)) {
    redirect('/dashboard');
  }

  return user;
}

// Usage in pages:
export default async function DashboardPage() {
  const user = await requireAuth();
  return <div>Welcome {user.name}</div>;
}

export default async function AdminPage() {
  const admin = await requireAdmin();
  return <div>Admin Panel</div>;
}

export default async function ModeratorPage() {
  const user = await requireRole(['admin', 'moderator']);
  return <div>Moderator Tools</div>;
}

// ✅ Reusable auth guards
// ✅ Type-safe user object
// ✅ Flexible role checking
// ✅ Clean page code

Example 2: Guest-Only Routes (Redirect if Authenticated)

app/login/page.tsx
import { redirect } from 'next/navigation';
import { cookies } from 'next/headers';

async function getSession() {
  const sessionCookie = cookies().get('session');
  if (!sessionCookie) return null;

  try {
    return await verifySession(sessionCookie.value);
  } catch {
    return null;
  }
}

export default async function LoginPage() {
  const session = await getSession();

  // Redirect to dashboard if already authenticated
  if (session) {
    redirect('/dashboard');
  }

  return (
    <div className="min-h-screen flex items-center justify-center">
      <LoginForm />
    </div>
  );
}

// Same pattern for:
// - /signup
// - /forgot-password
// - /reset-password

// ✅ Prevents authenticated users from seeing login
// ✅ Automatic redirect to dashboard
// ✅ Better UX - no unnecessary forms

Example 3: Subscription/Payment Guard

app/premium/page.tsx
import { redirect } from 'next/navigation';
import { requireAuth } from '@/lib/auth-guards';

async function getSubscription(userId: string) {
  const subscription = await db.subscriptions.findFirst({
    where: {
      userId,
      status: 'active',
    },
  });
  return subscription;
}

export default async function PremiumPage() {
  const user = await requireAuth();

  // Check subscription
  const subscription = await getSubscription(user.id);

  if (!subscription) {
    redirect('/pricing?upgrade=true');
  }

  return (
    <div className="container mx-auto px-4 py-8">
      <div className="bg-yellow-50 border border-yellow-200 p-4 rounded-lg mb-6">
        <p className="text-sm text-yellow-800">
          ✨ Premium Feature - Thanks for subscribing!
        </p>
      </div>

      <PremiumFeatures />
    </div>
  );
}

// ✅ Two-level protection: auth + subscription
// ✅ Redirect to pricing with upgrade parameter
// ✅ Clear premium indicator

Authentication & Guards Structure

Project organization with middleware and auth guards

appImportant

Select a file or folder to see details

Best Practices

1. Use Middleware for App-Wide Protection

TYPESCRIPT
// ✅ GOOD: Middleware protects multiple routes
export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*'],
};

// Efficient - runs once on Edge
// Protects entire sections

// ❌ BAD: Auth check in every page
// Repetitive, error-prone, harder to maintain

2. Always Use router.replace() After Login

TYPESCRIPT
// ✅ GOOD: Replace so user can't go back to login
router.replace('/dashboard');

// ❌ BAD: Push allows back button to login
router.push('/dashboard');
// User can press back and see login form again!

3. Preserve Intended Destination

TYPESCRIPT
// ✅ GOOD: Save where user was trying to go
redirect(`/login?redirect=${pathname}`);

// After login, redirect back:
router.replace(searchParams.get('redirect') || '/dashboard');

// ✅ Better UX - user returns to intended page

4. Clear Cookies on Invalid Session

TYPESCRIPT
// ✅ GOOD: Clear invalid cookies
const user = await verifyToken(token);
if (!user) {
  const response = NextResponse.redirect('/login');
  response.cookies.delete('session'); // Clear invalid cookie
  return response;
}

// Prevents repeated verification attempts

5. Use Appropriate Redirect Type

TYPESCRIPT
// ✅ Temporary redirects (307)
redirect('/login'); // Auth checks
redirect('/dashboard'); // Post-login

// ✅ Permanent redirects (308)
permanentRedirect('/blog'); // Moved content
permanentRedirect('/new-url'); // URL structure change

// Use permanent only when content truly moved permanently

Key Takeaways

  • redirect() - Server Components & Server Actions
  • router.replace() - Client Components (use after login)
  • Middleware - app-wide route protection
  • permanentRedirect() - for moved content (308 status)
  • Auth guards - reusable requireAuth() utilities
  • Role checks - verify user permissions
  • Preserve redirect - save intended destination
  • Two-step protection - authentication + authorization

🎉 Navigation Section Complete!

You've completed the Navigation and Links section! You've mastered:

  • ✅ Link component for client-side navigation
  • ✅ useRouter hook for programmatic navigation
  • ✅ usePathname and useSearchParams for URL state
  • ✅ Active links and navigation states
  • ✅ Redirects and navigation guards

You now have complete mastery of navigation in Next.js! You can build applications with smooth navigation, active link highlighting, loading states, and secure route protection. These skills are essential for creating professional, user-friendly Next.js applications with proper authentication flows.

🔒 Security First

Always implement authentication checks on the server (middleware or Server Components), not just the client. Client-side checks can be bypassed, but server-side checks ensure true security. Use middleware for efficiency and comprehensive protection!

Final Quiz: Navigation Mastery

Question 1 of 4

What's the difference between redirect() and router.replace()?

Master redirects and navigation guards in Next.js! Learn to protect routes and build secure navigation flows.

Previous
Active Links and Navigation States
Next
CSS Modules in Next.js

Continue Your Next.js Journey

Join 2,000+ developers mastering Next.js. Get more advanced tutorials on forms, APIs, and deployment - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

NextJS Tutorials

0 of 70 completed

Your Progress0%

Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo