Unit 2: Component Design and Styling
I. Orientation — Building Maintainable React Interfaces
React organizes a user interface as a hierarchy of declarative components: JavaScript functions that describe UI from current props and state. Effective component design separates responsibilities, encourages composition, and combines accessible HTML with predictable styling.
- Declarative model: A component describes what should appear for given data rather than manually changing individual DOM nodes.
- Component hierarchy: Applications form trees in which parents render children and pass data downward through props.
- One-way data flow: Data normally moves from parent to child; events communicate user actions upward through callback props.
- Purity convention: Rendering should not mutate props, state, or external values; the same inputs should produce the same JSX.
- Separation of concerns: Components may separate data coordination, interaction logic, visual presentation, and reusable behavior.
- State principle: Keep state minimal and place it in the nearest common owner of every component that needs it.
- Styling principle: Styling should be consistent, responsive, reusable, and independent of accidental markup details.
- Accessibility principle: Prefer semantic HTML and native controls before adding ARIA attributes or custom keyboard behavior.
II. Component Construction — Reusable UI Building Blocks
Component construction turns an interface into small units with explicit inputs, focused responsibilities, and composable output.
A. Functional components
Functional components are JavaScript functions that receive props and return renderable JSX.
- Definition: Component names begin with an uppercase letter so JSX distinguishes
<Greeting />from an HTML element such as<section>. - Input and output: Props enter as a function argument, while the return value may be JSX, a fragment, a string, a number,
null, or another renderable value. - Hooks: Functions such as
useStateadd React features without classes; hooks must be called unconditionally at the component’s top level. - Purity: Network requests, subscriptions, and DOM synchronization are side effects and belong in event handlers or effects, not directly in rendering.
function Greeting({ name }) {
return <h2>Hello, {name}</h2>;
}Here, name is an input prop and <h2> is the component’s rendered output.
B. Props-driven component reusability
Props make one component reusable by allowing its content, appearance, and behavior to vary through explicit inputs.
- Read-only inputs: A child must not assign to or mutate its props; the parent remains the owner of supplied values.
- Destructuring:
function Avatar({ src, alt, size = 48 })documents expected inputs and supplies a defaultsize. - Behavior props: Callback props such as
onSaveallow a reusable component to report events without knowing the parent’s business logic. - Stable API: Prefer meaningful props such as
variant="danger"over implementation-specific props such asredBackground. - Example:
<Button variant="primary" onClick={submit}>Save</Button>combines configuration, behavior, and nested content.
C. Component composition patterns
Composition builds complex interfaces by nesting components rather than creating large components or deep inheritance structures.
- Children composition: A wrapper receives nested JSX through the special
childrenprop, as in<Card><Profile /></Card>. - Named slots: Props such as
header,footer, oractionsprovide multiple insertion points whenchildrenalone is ambiguous. - Specialization: A specific component can configure a general one; for example,
DeleteButtonmay render a reusableButtonwith a destructive variant. - Render props: A function prop can delegate rendering, although hooks and ordinary composition are often simpler.
- Trade-off: Excessive nesting can obscure data flow, so each compositional layer should provide a clear structural or behavioral benefit.
D. Reusable component abstractions
A reusable abstraction captures a stable pattern while leaving changing data and behavior configurable.
- Good abstraction: A
Modalmay own focus management and dialog structure while receiving its title, body, and close callback. - Avoid duplication: Repeated markup, class combinations, or interaction rules can justify extraction into a shared component.
- Avoid premature generalization: Two visually similar elements do not require one component if their semantics or future behavior differ.
- Controlled API: Expose only necessary variation through props; too many Boolean props such as
small,blue, androundedcreate conflicting combinations. - Escape hatch: A restrained
classNameorchildrenprop can support extension without embedding every possible option in the abstraction.
III. Rendering and Data Ownership — Coordinating Component Hierarchies
React components remain predictable when rendering branches are explicit, shared state has one owner, and responsibilities follow clear boundaries.
A. Conditional rendering strategies
Conditional rendering selects JSX according to state or props by using ordinary JavaScript expressions.
- Early return:
if (loading) return <Spinner />;isolates a complete loading branch before the main interface. - Ternary operator:
{isLoggedIn ? <Dashboard /> : <Login />}suits an either-or choice because both outcomes are visible together. - Logical AND:
{error && <Alert>{error}</Alert>}renders an element only whenerroris truthy; numeric zero requires care because0 && ...renders0. - Null result: Returning
nullintentionally renders nothing, although the component still participates in React’s lifecycle. - Readability: Move complex conditions into named variables such as
const canEdit = owner && !archived;rather than nesting several operators in JSX.
B. Lifting state up in component hierarchies
Lifting state up moves shared state to the nearest common ancestor so sibling components use one authoritative value.
- Single source of truth: If
SearchInputchanges a query andResultsdisplays matching items, their parent should usually ownquery. - Downward data: The parent passes the current value to children through props.
- Upward events: The parent passes a setter or domain callback, such as
onQueryChange, for children to call. - Controlled component: The child displays a value supplied by its parent instead of maintaining a conflicting copy.
function SearchPage() {
const [query, setQuery] = useState("");
return <>
<SearchInput value={query} onChange={setQuery} />
<Results query={query} />
</>;
}- Cost: Lifting state too high causes unnecessary coupling and broad re-renders; keep private state local when no sibling needs it.
C. Defining component responsibility boundaries
A component boundary is effective when the component has one coherent reason to change and a clear public interface.
- Presentation responsibility: A
ProductCardcan focus on semantic markup and display props without fetching the entire catalogue. - Coordination responsibility: A page component may fetch records, handle errors, and distribute data to presentational children.
- Interaction responsibility: A form can own input state and validation while delegating generic controls to
TextFieldandButton. - Extraction signals: Repetition, difficult testing, deeply nested JSX, or a clearly named sub-interface can justify a new component.
- Boundary warning: Components that mix unrelated fetching, navigation, formatting, and layout become difficult to reuse and modify.
IV. Styling Systems — From React CSS Choices to Tailwind Utilities
React does not prescribe a styling system; teams select approaches that balance scope, consistency, runtime cost, and developer experience.
A. Styling approaches in React applications
React supports several styling approaches, each with different scoping and maintenance characteristics.
- Global CSS: Imported stylesheets suit resets and design tokens, but broad selectors can cause naming collisions.
- CSS Modules: Files such as
Card.module.cssgenerate locally scoped class names and preserve standard CSS features. - Inline styles:
style={{ color: "red" }}supports dynamic values but lacks ordinary pseudo-classes and media queries. - CSS-in-JS: Libraries can colocate dynamic styles with components, though they may add runtime or tooling costs.
- Utility classes: Systems such as Tailwind apply small classes directly in JSX and reduce the need to invent selector names.
- Class prop: React uses
className, notclass, because JSX properties follow DOM property conventions.
B. Tailwind CSS fundamentals
Tailwind CSS provides predefined utility classes that generate styles through a configured build process.
- Utility mapping:
p-4,font-semibold, androunded-lgrepresent padding, font weight, and border radius. - Design scale: Spacing and color classes use a shared theme, helping
mt-4mean the same spacing step throughout the application. - State variants: Prefixes such as
hover:,focus:, anddisabled:apply utilities under defined conditions. - Build detection: Tailwind scans source files for class names and emits the required CSS; dynamically constructed fragments such as
`text-${color}-500`may not be detected. - React usage: Conditional classes can be selected as complete strings:
active ? "bg-blue-600" : "bg-gray-200".
C. Utility-first styling methodology
Utility-first styling constructs designs by combining narrowly focused classes directly on elements.
- Traditional semantic CSS: A class such as
.profile-cardhides several declarations in a stylesheet. - Utility-first CSS:
className="rounded-lg bg-white p-6 shadow"makes the applied design visible beside the markup.
- Advantages: Utilities limit selector conflicts, encourage consistent scales, and make deletion safe because styles are tied closely to elements.
- Costs: Long class strings can reduce readability and repeated combinations can create duplication.
- Discipline: Arrange utilities consistently and extract a component when a repeated class set represents a genuine UI concept.
- Separation: Utility-first design does not eliminate abstraction; it relocates many low-level styling decisions from custom selectors to standardized classes.
D. Responsive design using Tailwind breakpoints
Tailwind uses mobile-first breakpoint variants to apply utilities from a minimum viewport width upward.
- Base styles: Unprefixed utilities apply to all viewport sizes and should describe the smallest layout first.
- Breakpoint override:
md:grid-cols-2takes effect at the configuredmdwidth and continues above it unless overridden later. - Common prefixes: Default installations commonly provide
sm,md,lg,xl, and2xl, although projects may customize their exact widths. - Example:
className="flex flex-col gap-4 md:flex-row"stacks items initially and changes to a row atmd. - Design principle: Select breakpoints where content needs layout change, not merely for named device categories.
E. Reusable UI styling patterns
Reusable styling patterns encode consistent visual states while preserving semantic component APIs.
- Variants: A
Buttoncan mapprimary,secondary, anddangerprops to complete, approved utility strings. - Class composition: Helpers such as
clsxcombine base, conditional, and caller-provided classes without manual spacing errors. - Design tokens: Theme colors, spacing, typography, and radii ensure repeated components follow one visual system.
- State coverage: Shared controls should define hover, focus-visible, disabled, loading, and error appearances rather than only a default state.
- Conflict control: Keep variant maps explicit so mutually incompatible utilities are not applied accidentally.
- Abstraction test: Extract repeated styling when it represents a stable concept such as
BadgeorAlert, not merely because two elements sharep-4.
V. Inclusive Markup — Accessible and Semantic Interfaces
Accessible React interfaces use correct HTML foundations, perceivable content, keyboard operation, and programmatic relationships.
A. Accessibility fundamentals
Accessibility enables people using keyboards, screen readers, magnification, voice control, or other assistive technologies to operate the interface.
- Keyboard access: Interactive controls must be reachable and usable without a pointer; native
<button>elements already support focus and Enter/Space activation. - Visible focus: Tailwind classes such as
focus-visible:ring-2may enhance focus indication, but focus outlines should not be removed without replacement. - Accessible names: Inputs require associated
<label>elements, while icon-only buttons need a name such asaria-label="Close". - Image alternatives: Meaningful images need informative
alttext; decorative images generally usealt="". - Color and status: Do not communicate errors only through red color; include text and, where appropriate,
aria-describedby. - ARIA rule: Use ARIA to supplement missing semantics, not to replace suitable native HTML behavior.
B. Semantic HTML practices in React
Semantic HTML communicates document structure and control meaning to browsers and assistive technologies.
- Landmarks: Use
<header>,<nav>,<main>,<aside>, and<footer>according to their roles rather than wrapping everything in<div>. - Heading order: Use headings to express hierarchy—typically an
<h1>followed by logically nested<h2>and<h3>sections. - Native controls: Use
<button>for actions and<a href="...">for navigation; a clickable<div>lacks equivalent keyboard and semantic behavior. - Lists and tables: Repeated list items belong in
<ul>or<ol>; tabular relationships require<table>, headings, rows, and cells. - Forms: Connect labels with controls using
htmlForandid, because React useshtmlForin JSX. - Preserved semantics: Styling a semantic element with Tailwind—such as
<button className="...">—changes presentation without discarding its built-in accessibility.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →