Code To Learn logo

Code To Learn

M7: Forms & Authentication

L6: Add SignInPage to Router

Configure routing for authentication

Now let's make the sign-in page accessible through React Router! 🛤️

Why Add a Route?

Benefits of /sign-in route:

  • ✅ Clean URL (sharable, bookmarkable)
  • ✅ Browser history works (back/forward buttons)
  • ✅ Easy to redirect users
  • ✅ SEO-friendly (if needed)
  • ✅ Professional standard

User experience:

Before: No way to access sign-in page
After:  User can visit yourdomain.com/sign-in

Update Router Configuration

Add the sign-in route to your existing router:

src/App.jsx
import { Routes, Route } from 'react-router-dom';
import HomePage from '@/pages/HomePage';
import FavoritesPage from '@/pages/FavoritesPage';
import NotFoundPage from '@/pages/NotFoundPage';
import SignInPage from '@/pages/SignInPage';  // ← New import
import { useAuth } from '@/contexts/AuthContext';

function AppContent() {
  const { user, isLoading } = useAuth();

  if (isLoading) {
    return (
      <div className="loading-container">
        <div className="spinner"></div>
        <p>Loading...</p>
      </div>
    );
  }

  return (
    <div className="app">
      {user && <Navbar />}
      <main className="main-content">
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/favorites" element={<FavoritesPage />} />
          <Route path="/sign-in" element={<SignInPage />} />  {/* ← New route */}
          <Route path="*" element={<NotFoundPage />} />
        </Routes>
      </main>
    </div>
  );
}

function App() {
  return (
    <AuthProvider>
      <BrowserRouter>
        <AppContent />
      </BrowserRouter>
    </AuthProvider>
  );
}

export default App;

Understanding Route Order

Test the Route

Start your development server:

npm run dev

Visit the sign-in page:

Open browser and navigate to:

http://localhost:5173/sign-in

You should see your beautiful sign-in page! 🎉

Test navigation:

Try these URLs manually:

  • http://localhost:5173/ → HomePage
  • http://localhost:5173/sign-in → SignInPage
  • http://localhost:5173/favorites → FavoritesPage
  • http://localhost:5173/unknown → NotFoundPage (404)

Check browser history:

  1. Visit /
  2. Visit /sign-in
  3. Click browser back button
  4. Should return to /

History works!

Make it easy to get to sign-in page:

Add prominent button on homepage:

src/pages/HomePage.jsx
import { useNavigate } from 'react-router-dom';

function HomePage() {
  const navigate = useNavigate();

  return (
    <div className="home-page">
      <div className="hero-section">
        <h1>Welcome to Holidaze</h1>
        <p>Find your perfect vacation rental</p>
        
        <button 
          onClick={() => navigate('/sign-in')}
          className="cta-button"
        >
          Get Started
        </button>
      </div>
      
      {/* Rest of homepage */}
    </div>
  );
}

Button styling:

src/app/global.css
.cta-button {
  background: #3b82f6;
  color: white;
  padding: 1rem 2rem;
  font-size: 1.125rem;
  font-weight: 600;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s;
}

.cta-button:hover {
  background: #2563eb;
  transform: translateY(-2px);
  box-shadow: 0 10px 20px rgba(59, 130, 246, 0.3);
}

Result: Big, obvious call-to-action!

Automatically redirect when needed:

src/components/ProtectedComponent.jsx
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '@/contexts/AuthContext';

function ProtectedComponent() {
  const { user } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    if (!user) {
      // User not signed in, redirect to sign-in page
      navigate('/sign-in');
    }
  }, [user, navigate]);

  if (!user) {
    return null; // Or loading spinner
  }

  return (
    <div>
      {/* Protected content */}
    </div>
  );
}

When to use:

  • Protecting specific features
  • After sign-out
  • When session expires

Better approach (coming in L13-14):

We'll create a proper <Protected> wrapper component!

Common Routing Patterns

For complex page structures:

<Routes>
  {/* Top-level routes */}
  <Route path="/" element={<HomePage />} />
  
  {/* Nested routes (layout) */}
  <Route path="/dashboard" element={<DashboardLayout />}>
    <Route index element={<DashboardHome />} />
    <Route path="settings" element={<Settings />} />
    <Route path="profile" element={<Profile />} />
  </Route>
</Routes>

URLs created:

  • /dashboard → DashboardHome
  • /dashboard/settings → Settings
  • /dashboard/profile → Profile

All share DashboardLayout wrapper!

Example layout:

function DashboardLayout() {
  return (
    <div className="dashboard">
      <Sidebar />
      <main>
        <Outlet />  {/* Nested route renders here */}
      </main>
    </div>
  );
}

For dynamic content:

<Routes>
  <Route path="/venues/:id" element={<VenuePage />} />
  <Route path="/user/:username" element={<ProfilePage />} />
</Routes>

Access parameters:

import { useParams } from 'react-router-dom';

function VenuePage() {
  const { id } = useParams();
  
  // URL: /venues/123
  // id = "123"
  
  return <div>Viewing venue {id}</div>;
}

Multiple parameters:

<Route path="/cities/:country/:city" element={<CityPage />} />

// URL: /cities/norway/oslo
// country = "norway"
// city = "oslo"

For filters, search, pagination:

// URL: /search?query=beach&guests=4&page=2

Access query parameters:

import { useSearchParams } from 'react-router-dom';

function SearchPage() {
  const [searchParams, setSearchParams] = useSearchParams();
  
  const query = searchParams.get('query');      // "beach"
  const guests = searchParams.get('guests');    // "4"
  const page = searchParams.get('page');        // "2"
  
  return <div>Searching for: {query}</div>;
}

Update query parameters:

// Add/update parameter
setSearchParams({ query: 'mountains', guests: '2' });
// URL becomes: /search?query=mountains&guests=2

// Keep existing, add new
setSearchParams(prev => {
  prev.set('page', '3');
  return prev;
});
// URL becomes: /search?query=mountains&guests=2&page=3

Navigate with code (not just links):

import { useNavigate } from 'react-router-dom';

function MyComponent() {
  const navigate = useNavigate();
  
  const handleSuccess = () => {
    // After successful action, go somewhere
    navigate('/dashboard');
  };
  
  const handleCancel = () => {
    // Go back one step in history
    navigate(-1);
  };
  
  const handleError = () => {
    // Replace current history entry (can't go back)
    navigate('/error', { replace: true });
  };
  
  return (
    <div>
      <button onClick={handleSuccess}>Finish</button>
      <button onClick={handleCancel}>Cancel</button>
    </div>
  );
}

Use cases:

  • After form submission → redirect
  • After sign-in → go to dashboard
  • After sign-out → go to home
  • Cancel button → go back
  • Error handling → redirect to error page

Troubleshooting Routes

What's Next?

In Lesson 7, we'll:

  1. Create SignInForm component with React Hook Form
  2. Add Zod schema validation
  3. Build beautiful form fields
  4. Handle form state and errors

✅ Lesson Complete! Sign-in page is now accessible via /sign-in route!

Key Takeaways

  • Routes map URLs to components (/sign-inSignInPage)
  • Route order matters (specific before catch-all)
  • Catch-all * route handles 404 errors
  • Import component before adding to Routes
  • Test navigation with browser URL bar and history
  • Add links to make routes accessible
  • Use navigate() for programmatic navigation
  • Protected routes coming in later lessons