Lesson Progress
0% Complete

Modern front-end frameworks (like React, Vue, Angular, and Svelte) help you build websites and web apps by breaking the interface into small, reusable parts called components. You also need a clear way to handle navigation between views (routing) and a project structure that stays clean as your codebase grows.


1) What is a Component?

A component is a self-contained piece of the UI that includes:

  • Structure (what it renders)
  • Behaviour (how it responds to user actions)
  • Styling (how it looks)

Think of components as building blocks. Instead of writing one large page, you build many small pieces and compose them into screens.

Examples of common components

  • Button
  • InputField
  • Navbar
  • Footer
  • Card
  • Modal
  • ProductList
  • LoginForm

Why components matter

Components improve:

  • Reusability: build once, use many times
  • Consistency: same UI patterns across the site
  • Maintainability: changes are isolated
  • Testability: smaller units are easier to test

2) Component Design: “Reusable vs Specific”

A useful way to design components is to separate them into:

A) Reusable (generic) UI components

These are not tied to one feature. They can be used anywhere.

Examples:

  • Button
  • TextField
  • Alert
  • Spinner
  • Card

Characteristics:

  • Accept configuration through props (inputs)
  • Avoid hard-coded text and layout assumptions
  • Are focused on one job (single responsibility)

B) Feature (domain) components

These are tied to a specific business feature.

Examples:

  • CheckoutSummary
  • UserProfilePanel
  • ProductFilterSidebar

Characteristics:

  • Combine multiple generic components
  • Contain feature-specific logic
  • Often connected to state and API data

3) Props, Slots, and Composition (Building Flexible UI)

Different frameworks use different terms, but the idea is similar: pass data into a component and allow it to render flexible content.

Props (inputs)

Props allow the parent to control a child component.

Typical uses:

  • button label: label="Save"
  • visual style: variant="primary"
  • disabled state: disabled={true}
  • event callbacks: onClick={handleSave}

Guideline: avoid components reading global state directly unless they are truly “smart”/container components. Keep most UI components “dumb” and configurable.

Composition

Instead of creating many variations of the same component, you can build a base component and compose it.

Example approach:

  • Card as a layout container
  • CardHeader, CardBody, CardFooter as optional parts
  • Or allow the parent to pass inner content

Composition reduces duplication and keeps the UI system consistent.


4) Reusable UI Patterns You’ll Use Often

A) Buttons (variants and states)

A good button component should support:

  • Variants: primary, secondary, danger, link
  • States: default, hover, focus, disabled, loading
  • Accessibility: correct element type and focus styling

Accessibility note: Use a real <button> for actions and an <a> for navigation. Don’t use a <div> that “acts like a button”.

B) Form inputs with validation

Reusable form controls typically include:

  • Label
  • Help text
  • Error message
  • Required indicator
  • Accessible connection between label and input (for / id or framework equivalent)

Accessibility note: ensure error messages are announced to screen readers (for example, using an ARIA live region where appropriate).

C) Lists and keys (stable identity)

When rendering lists of items, frameworks rely on keys to track items efficiently.

Guideline: use stable IDs (like a database ID), not array indexes, to avoid UI bugs when items are added/removed/reordered.

D) Modals, toasts, and overlays

These often look simple but require careful handling:

  • Focus should move into the modal when it opens
  • Focus should return to the triggering element when it closes
  • Escape key should close (if appropriate)
  • Background content should not be reachable by keyboard while modal is open

5) Smart vs Dumb Components (Container vs Presentational)

A common scalable approach:

Presentational components (“dumb”)

  • Focus on UI only
  • Receive data and callbacks via props
  • Easy to reuse and test

Container components (“smart”)

  • Fetch data from APIs
  • Read/write state
  • Handle side effects (loading, errors)
  • Pass processed data down to presentational components

This separation prevents your UI components from becoming tightly coupled to your data layer.


6) Routing: Navigation in Single Page Applications (SPAs)

In an SPA, navigation is handled by a router. Instead of loading a new HTML page from the server each time, the app updates the view based on the URL.

What routing provides

  • Map URLs to views/screens (routes)
  • Support nested layouts (e.g., dashboard with side navigation)
  • Enable route parameters (/products/:id)
  • Manage “not found” pages (404)
  • Control access (protected routes)

Key concepts

  • Route: a URL pattern linked to a component/view
  • Link: a router-aware navigation element (prevents full reload)
  • Route parameters: variables in the URL (like an ID)
  • Query string: filters/sorting (?sort=price&category=shoes)
  • Nested routes: child routes rendered inside parent layouts

7) Route Structure for Real Projects

A clean route structure often mirrors features and layouts.

Example patterns you’ll see:

  • Public pages: /, /about, /pricing
  • Auth: /login, /register, /forgot-password
  • App area: /app/... or /dashboard/...
  • Resource pages: /products, /products/:id, /products/:id/edit

Layout routes (shared UI)

Many frameworks support a route that renders a layout plus a nested outlet for child pages.

Examples of shared layout components:

  • MainLayout (header + footer)
  • DashboardLayout (side nav + top bar)
  • AuthLayout (simple centred form layout)

This prevents repeating navigation and layout markup across pages.


8) Protected Routes (Authentication and Authorisation)

Some pages should only be available to logged-in users (authentication) or to certain roles (authorisation).

Common approach:

  • Use a route guard / wrapper that checks:
    • Is the user logged in?
    • Do they have the required role/permissions?
  • If not:
    • Redirect to login
    • Or show an “Access denied” page

Good practice: don’t rely only on front-end route protection. The back-end must enforce permissions too. Front-end checks are mainly for user experience.


9) Routing and Data: Loading, Errors, and Empty States

When a route depends on API data (e.g., /products/:id):

  • Show a loading state (spinner/skeleton)
  • Handle error states (network errors, 404, server issues)
  • Handle empty states (no data yet)

Design these states as reusable UI components:

  • LoadingSpinner or SkeletonCard
  • ErrorMessage
  • EmptyState

This keeps pages consistent and reduces duplicated logic.


10) Project Structure for Scalability and Maintainability

A framework gives you tools, but you still need a clear structure.

Typical folder groupings (conceptually)

  • components: reusable UI (buttons, inputs, cards)
  • features/pages: route-level screens and feature-specific components
  • layouts: shared page structures
  • routes: routing configuration
  • services/api: API clients, request helpers, endpoints
  • state: state management (stores, slices, reducers)
  • utils: helpers (formatting, validation)
  • styles: global styles, variables, themes
  • assets: images, icons, fonts
  • tests: unit/integration tests

Guideline: group by feature when the app grows. Small projects can group by type (components, utils, services), but large projects often scale better with feature-based structure.


11) Creating a Reusable UI System (Design Tokens and Consistency)

To avoid inconsistent styling (10 different blues, 6 button sizes), define:

  • Design tokens: colours, spacing, font sizes, border radius
  • Component variants: clear rules for primary/secondary actions
  • Consistent spacing system: e.g., 4px or 8px scale

Whether you use CSS variables, a utility framework, or a component library, the goal is the same: consistent, maintainable UI.


12) Accessibility and Reusability (Important Together)

Reusable components must be accessible by default, otherwise you repeat accessibility problems everywhere.

Key checklist:

  • Keyboard support (tab, enter, space, escape where relevant)
  • Visible focus states
  • Correct HTML semantics (buttons, links, headings)
  • Labels for form inputs
  • ARIA only when needed (don’t “ARIA over” bad HTML)
  • Colour contrast meets WCAG guidelines

Tip: treat accessibility as part of the component definition, not a “later fix”.


13) Practical Task (Hands-on)

Build a small component set and connect it with routing:

  1. Create reusable components:

    • Button (variants + disabled + loading)
    • TextField (label + error)
    • Card (simple container)
    • Alert (success/error/info)
  2. Create pages (route-level):

    • Home page (/)
    • Products list (/products)
    • Product details (/products/:id)
    • Not found page (* or /404)
  3. Add navigation:

    • Navbar with router links
    • Active link styles (show the current page)
  4. Add states:

    • Loading and error UI when fetching product data
    • Empty state if no products exist

Deliverable: a small SPA that demonstrates clean component design, working navigation, and consistent reusable UI.


Key Takeaways

  • Components help you build UIs in small, reusable pieces.
  • Reusable components should be flexible (props/composition) and accessible by default.
  • Routing connects URLs to views without full page reloads.
  • A good project structure and shared layouts keep your codebase scalable and easy to maintain.