Components, Templates, JSX, and CompositionIntroductionModern frontend applications are not built as one large page. They are assembled from smaller interface units such as navigation bars, forms, product cards, dialogs, tables, and dashboards. In React and Vue, these units are called components. A component combines structure, presentation, and—where appropriate—interaction into a reusable unit with a defined contract. This chapter examines the component models of React.js and Vue.js. React uses JavaScript and JSX to describe user interfaces, whereas Vue commonly uses Single-File Components (SFCs), which place template, logic, and styling in one .vue file. Although their syntax differs, both frameworks encourage the same broad design principles: decompose interfaces, pass data explicitly, emit events deliberately, preserve one-way data flow, and compose simple components into larger systems. A useful component is not merely a fragment of markup. It is an abstraction with responsibilities, inputs, outputs, constraints, and an expected visual and behavioral result. Understanding these contracts is essential for maintainable frontend engineering. Learning ObjectivesBy the end of this chapter, you should be able to: - explain the purpose and anatomy of React and Vue components;
- write React components using JSX and Vue components using templates;
- distinguish props, children, slots, local state, and emitted events;
- implement conditional rendering, list rendering, and stable keys;
- compare styling approaches used in both ecosystems;
- compose components while preserving clear component contracts;
- identify common design errors and evaluate component quality in a professional codebase.
1. Components as a Design ModelA component can be understood as a function of inputs and internal state that produces a user-interface result. Conceptually: UI = f(inputs, state) Here, UI is the rendered interface, inputs are externally supplied values such as props, and state is data managed inside the component. The expression is a design model rather than a literal implementation rule. It emphasizes that rendering should be predictable: the same relevant inputs and state should produce the same result. Components usually have four elements: - Structure — the elements displayed to the user.
- Data inputs — values supplied by a parent component.
- Behavior — event handlers, state changes, and effects.
- Contract — documented expectations about accepted inputs and emitted outputs.
A component tree represents the hierarchy of an application. A page may contain a Dashboard, which contains Sidebar, Header, and ContentArea; the content area may contain Card, Table, and Button components. This hierarchy supports local reasoning: developers can understand one component without reading the entire application. Component boundariesA good boundary often appears where one of the following is true: - a visual pattern is repeated;
- a unit has a clear responsibility;
- a section has its own state or interaction;
- a team needs to test or change the unit independently;
- the markup would otherwise become difficult to read.
Overly small components can obscure the design through unnecessary indirection. Overly large components become difficult to test and reuse. Componentization is therefore a design judgment, not a race to create the greatest number of files. 2. React Components and JSXA React function component is a JavaScript function that returns a description of the interface. A minimal example is: function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}The component receives an object of props. Destructuring { name } extracts one property. The returned JSX resembles HTML, but it is JavaScript syntax transformed by a build tool into React element descriptions. A component can also be written with an explicit props parameter: function ProductTitle(props) {
return <h2>{props.title}</h2>;
}Function components should begin with an uppercase letter when used as components. Lowercase names are interpreted as intrinsic HTML elements. Thus, <ProductTitle /> refers to a component, while <producttitle /> is generally treated as an unknown HTML element. JSX expressions and attributesJSX permits JavaScript expressions inside curly braces: function Price({ amount, currency }) {
return <span>{currency} {amount.toFixed(2)}</span>;
}Expressions may contain variables, property access, function calls, and conditional expressions. Statements such as if and for cannot be placed directly inside JSX, although they can be used before the return statement or represented through expressions. JSX attributes use camelCase for many DOM properties: <button className="primary" onClick={handleClick} disabled={isSaving}>
Save
</button>className corresponds to the HTML class attribute, and onClick receives a function. The function must be passed rather than called during rendering. onClick={handleClick} registers the handler; onClick={handleClick()} calls it immediately while rendering and usually produces incorrect behavior.
A component must return one root value. A wrapper element may be used, or a fragment may group elements without adding a DOM node: function UserSummary({ user }) {
return (
<>
<h2>{user.name}</h2>
<p>{user.email}</p>
</>
);
}JSX is not a security boundaryValues inserted through ordinary JSX text are escaped by React. This reduces the risk of interpreting user input as HTML. However, APIs that deliberately inject raw HTML, such as dangerouslySetInnerHTML, bypass this protection and must only be used with trusted or properly sanitized content. 3. Vue Single-File Components and TemplatesA Vue Single-File Component conventionally contains three sections: <script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">Clicked {{ count }} times</button>
</template>
<style scoped>
button { padding: 0.5rem 1rem; }
</style>The <template> describes the rendered structure, <script setup> contains component logic, and <style> contains presentation rules. Build tooling compiles the file into a JavaScript component. Vue templates use HTML-like syntax with directives. Interpolation uses double curly braces, as in {{ count }}. Attribute binding uses :, and event binding uses @: <img :src="imageUrl" :alt="imageDescription" />
<button @click="submitForm">Submit</button> Vue expressions in templates are intended for concise presentation logic. Complex transformations should generally be moved into computed properties or methods rather than embedded in long expressions. A component can receive props with defineProps: <script setup>
const props = defineProps({
title: { type: String, required: true },
highlighted: { type: Boolean, default: false }
})
</script>
<template>
<article :class="{ highlighted: props.highlighted }">
<h2>{{ props.title }}</h2>
<slot />
</article>
</template>In <script setup>, declared props are also available directly in the template. Vue's prop declarations provide runtime validation in development and communicate the intended API to other developers. 4. Component Contracts: Props, Children, Slots, and EventsA component contract specifies what a component accepts and how it communicates. A strong contract is explicit, minimal, and stable. React props and childrenReact props are read-only from the child component's perspective. A child should not mutate a prop in order to communicate with its parent. Instead, the parent passes a callback: function SearchBox({ value, onChange, onSubmit }) {
return (
<form onSubmit={onSubmit}>
<input value={value} onChange={event => onChange(event.target.value)} />
<button type="submit">Search</button>
</form>
);
}The children prop contains content placed between a component's opening and closing tags: function Panel({ title, children }) {
return (
<section className="panel">
<h2>{title}</h2>
<div className="panel-body">{children}</div>
</section>
);
}This pattern is called composition. The Panel owns the container structure but does not need to know whether its body contains text, a form, or a list. React can support named regions by passing separate elements as ordinary props: function Layout({ header, sidebar, children }) {
return (
<div className="layout">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
}Vue props, slots, and emitsVue uses slots for content inserted by a parent. The default slot is equivalent in purpose to React's children: <Panel title="Account">
<p>Account settings are available here.</p>
</Panel> The receiving component renders the content with <slot />. Named slots support multiple content regions: <article class="card">
<header><slot name="header" /></header>
<div><slot /></div>
<footer><slot name="footer" /></footer>
</article> The parent supplies them with #header, the default slot, and #footer templates. Vue child-to-parent communication commonly uses custom events. In <script setup>: <script setup>
const emit = defineEmits(['save'])
function save() {
emit('save', { confirmed: true })
}
</script>
<template>
<button @click="save">Save</button>
</template>The parent listens with @save="handleSave". Events should describe something that happened, such as save, close, or selected, rather than directly instructing a distant ancestor to manipulate internal details. One-way data flowBoth frameworks favor one-way data flow: data moves from parent to child, while child components communicate upward through callbacks or events. This makes ownership visible. If a value needs to be changed, the component that owns it changes it and passes the new value downward. A common error is to copy a prop into local state without a deliberate synchronization strategy. The copied value can become stale. Local state is appropriate when the child owns an independent transient concern—for example, whether a dropdown is open—but not as an accidental duplicate of parent state. 5. Conditional RenderingInterfaces often have different states: loading, empty, successful, and error states. These states should be represented explicitly. React uses JavaScript control flow: function Results({ loading, error, items }) {
if (loading) return <p>Loading…</p>;
if (error) return <p role="alert">Unable to load results.</p>;
if (items.length === 0) return <p>No results found.</p>;
return <ResultList items={items} />;
}The logical AND operator is useful for a simple conditional, but it can produce unintended output when the left side is a number. Prefer an explicit Boolean condition such as items.length > 0 rather than {items.length && ...}. Vue provides directives such as v-if, v-else-if, and v-else: <template>
<p v-if="loading">Loading…</p>
<p v-else-if="error" role="alert">Unable to load results.</p>
<p v-else-if="items.length === 0">No results found.</p>
<ResultList v-else :items="items" />
</template> v-if conditionally creates and removes content. v-show keeps the element in the DOM and changes its CSS display behavior. v-show is appropriate when visibility changes frequently and retaining the element is beneficial; v-if is generally preferable when the condition is infrequent or the content is expensive.
6. Rendering Lists and the Role of KeysReact lists are commonly created with map: function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}Vue uses v-for and :key: <template>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</template>A key identifies the logical identity of an item across renders. It should be stable, unique among sibling items, and derived from domain identity, such as a database identifier. Array indexes are poor keys when items can be inserted, deleted, sorted, or edited because an index identifies a position rather than an item. Keys do not merely remove warnings. They affect how framework reconciliation preserves component instances and DOM state. An unstable key can cause an input's value or focus state to appear attached to the wrong record after a reorder. 7. Styling ComponentsSeveral styling strategies are common in both ecosystems: | Approach | Strength | Risk or limitation |
|---|
| Global CSS | Simple and widely understood | Naming collisions and unintended effects | | CSS Modules | Locally scoped class names | Requires build-tool support and conventions | | CSS-in-JS | Styles can follow component logic | Runtime or tooling complexity varies | | Utility classes | Fast, consistent composition | Markup can become visually dense | | Scoped Vue styles | Convenient component-local CSS | Scope does not replace architectural discipline |
React has no single official styling mechanism. A component may import a CSS file, use CSS Modules, or use a library. Vue supports ordinary styles and the scoped attribute. Scoped styles are transformed so that rules target elements associated with the component, but global selectors, inherited properties, and deep child-component behavior still require care. Styling should preserve accessibility. Visual states such as error, selected, and disabled should not be communicated by color alone. Focus indicators must remain visible, and interactive elements should use semantic controls rather than clickable generic containers. 8. Composition and Reuse PatternsComposition means building complex behavior by combining components rather than creating one component with many modes. For example, a reusable Modal can own focus management, escape-key handling, and backdrop behavior, while its parent supplies the title and body. A component with many Boolean props may signal an unclear abstraction: <Card compact bordered elevated dismissible ... /> Some options are legitimate, but a growing collection of flags can create a combinatorial testing problem. Composition often produces clearer alternatives, such as separate CardHeader, CardBody, and CardFooter components or a small set of named variants. Headless componentsA headless component supplies behavior without imposing a complete visual design. For example, a menu component may manage open state, keyboard navigation, and selected items while exposing slots or render content for the application. This separates interaction logic from visual branding and is useful in design systems. Controlled and uncontrolled componentsA controlled component receives its important value from its parent and reports changes upward. An uncontrolled component manages that value internally, exposing an initial value and events when necessary. Controlled components provide centralized authority and are useful for validation and synchronization; uncontrolled components can be simpler for isolated interactions. A library should document which model it uses and avoid silently switching between them. Composition versus inheritanceUI frameworks generally favor composition over class inheritance. Composition allows a component to contain, configure, or wrap other components without creating rigid taxonomies. Inheritance can be appropriate in some object-oriented domains, but it is rarely the best primary mechanism for frontend view reuse. 9. A Detailed Applied Scenario: An Accessible Course CatalogueThe following is an illustrative case, not a report of a specific organization. BackgroundA university wants a course catalogue displaying course cards, filtering controls, loading feedback, and an enrollment action. The catalogue must be usable on mobile devices and must allow the visual design to change without rewriting data-fetching logic. ProblemA first implementation placed fetching, filtering, card markup, modal behavior, and CSS in one page component. It had three problems: changes to card design risked breaking filters, list items used array indexes as keys, and the enrollment dialog could not be reused elsewhere. AnalysisThe domain contains distinct responsibilities: - the page owns the catalogue data and filter state;
- the filter form reports changes;
- the list renders a collection;
- the card presents one course;
- the dialog owns interaction details but receives content and callbacks.
The page should therefore own shared state, while presentational components should receive explicit props and emit events. React approachfunction CourseCard({ course, onEnroll }) {
return (
<article className="course-card">
<h2>{course.title}</h2>
<p>{course.description}</p>
<button type="button" onClick={() => onEnroll(course.id)}>
Enroll
</button>
</article>
);
}
function CourseList({ courses, onEnroll }) {
if (courses.length === 0) return <p>No courses match the selected filter.</p>;
return (
<div className="course-grid">
{courses.map(course => (
<CourseCard key={course.id} course={course} onEnroll={onEnroll} />
))}
</div>
);
}The list owns collection rendering and the stable key. The card owns course presentation. The page owns the enrollment action because enrollment may update global application state. Vue approach<!-- CourseCard.vue -->
<script setup>
defineProps({
course: { type: Object, required: true }
})
const emit = defineEmits(['enroll'])
</script>
<template>
<article class="course-card">
<h2>{{ course.title }}</h2>
<p>{{ course.description }}</p>
<button type="button" @click="emit('enroll', course.id)">
Enroll
</button>
</article>
</template><!-- CourseList.vue -->
<template>
<p v-if="courses.length === 0">No courses match the selected filter.</p>
<div v-else class="course-grid">
<CourseCard
v-for="course in courses"
:key="course.id"
:course="course"
@enroll="$emit('enroll', $event)"
/>
</div>
</template>The two implementations express the same architecture with different communication syntax. OutcomeThe revised design allows the card to be tested with fixed course data, the list to be tested with empty and non-empty collections, and the page to test filtering and enrollment behavior separately. A new visual card can be introduced without changing the catalogue's data model. Stable keys preserve input and focus identity when filtering or sorting changes the collection. Learning points- State ownership should be placed at the lowest common ancestor that needs the data.
- Events should communicate meaningful domain actions rather than expose internal DOM operations.
- Stable identifiers are essential for dynamic collections.
- Composition supports visual redesign and reuse.
- Accessibility and component boundaries should be designed together, not added after implementation.
10. Testing, Accessibility, and Professional PracticeComponent tests should verify observable behavior rather than framework implementation details. Useful tests include: - rendering required content from supplied props;
- displaying loading, error, and empty states;
- emitting or invoking the correct event with the correct payload;
- preserving keyboard access and visible focus;
- rendering lists with correct identity and accessible names.
A button should be used for an action, a link for navigation, and a heading for document structure. Components should expose labels for form controls, associate error messages with inputs, and use suitable ARIA attributes only when native HTML semantics are insufficient. Component contracts should be documented through TypeScript types, Vue prop definitions, runtime validation where appropriate, or a component documentation system. A contract should specify required values, optional defaults, event payloads, slot expectations, and accessibility obligations. 11. Challenges and LimitationsComponent systems do not automatically produce good architecture. Common difficulties include: - Prop drilling: passing data through several layers that do not use it. Context in React or dependency-injection/provide–inject patterns in Vue can help, but global access should not replace clear ownership.
- Over-abstraction: creating generic components before repeated requirements are understood.
- State duplication: keeping conflicting versions of the same value in parent and child.
- Unstable identity: using indexes or generated values as list keys.
- Hidden coupling: relying on a child’s internal DOM structure or undocumented classes.
- Performance misunderstandings: memoization and caching should be introduced after identifying a real rendering cost; they do not repair unclear state ownership.
- Accessibility debt: visually polished components can still fail keyboard, screen-reader, contrast, or reduced-motion requirements.
AI-assisted coding can generate component scaffolding, but generated code must be reviewed for event semantics, data leakage, unsafe HTML insertion, accessibility, dependency licensing, and consistency with the application's contract. AI does not establish whether a component boundary is appropriate; that remains an engineering judgment. 12. Best Practices Checklist- Keep components focused on a coherent responsibility.
- Define props and event payloads explicitly.
- Treat props as read-only.
- Keep state at the smallest appropriate owner.
- Prefer composition to a large collection of Boolean modes.
- Use stable domain identifiers as keys.
- Represent loading, empty, success, and error states deliberately.
- Use semantic HTML and keyboard-accessible interactions.
- Avoid raw HTML insertion unless content is trusted and sanitized.
- Choose styling conventions that the whole team can maintain.
- Test public behavior and document component contracts.
- Refactor only after observing repetition, complexity, or a clear reuse need.
Chapter SummaryReact and Vue both model interfaces as trees of reusable components. React components return JSX, which combines JavaScript expressions with HTML-like structure. Vue Single-File Components organize template, script, and style in a .vue file and use directives such as v-if, v-for, :, and @. Props provide parent-to-child inputs. React uses children for compositional content and callback props for upward communication; Vue uses slots for inserted content and emitted events for child-to-parent communication. These mechanisms support one-way data flow and make ownership explicit. Conditional rendering should represent meaningful interface states. Lists require stable, unique keys based on item identity, not position. Styling may use global CSS, modules, utilities, CSS-in-JS, or Vue-scoped styles, but accessibility and maintainability must guide the choice. Strong component architecture depends on clear contracts, purposeful boundaries, composition, semantic HTML, and tests focused on observable behavior. Key TermsComponent: A reusable unit that combines interface structure, behavior, and a contract. JSX: JavaScript syntax used by React to describe interface elements. Single-File Component: A Vue file that commonly contains template, script, and style sections. Props: Read-only values supplied by a parent component. Children: React content placed between a component’s opening and closing tags. Slot: A Vue insertion point for content supplied by a parent. Event: A notification that an interaction or domain action occurred. Composition: Building larger components by combining smaller components. One-way data flow: Movement of data from parent to child with upward communication through callbacks or events. Conditional rendering: Displaying different interface content according to state. Key: A stable identifier used to preserve item identity during list updates. Controlled component: A component whose important value is owned by its parent. Uncontrolled component: A component that manages an important value internally. Headless component: A behavior-oriented component with minimal imposed visual presentation. Prop drilling: Passing data through intermediate components that do not use it. Scoped style: Vue styling transformed to target elements associated with a component. Component contract: The documented inputs, outputs, structure, and behavioral expectations of a component. Semantic HTML: HTML whose elements communicate their intended meaning and behavior. Review QuestionsShort Answer Questions- What is the difference between a React prop and local component state?
- What role does
children play in React composition? - What are Vue slots used for?
- Why should list keys be stable and unique?
- Distinguish
v-if from v-show.
Long Answer Questions- Explain how one-way data flow operates in React and Vue, including the mechanisms used for child-to-parent communication.
- Compare JSX with Vue templates in terms of expressions, event binding, and conditional rendering.
- Discuss how component boundaries influence maintainability, testing, and reuse.
- Explain controlled and uncontrolled components and identify a suitable use case for each.
- Evaluate the major styling approaches available in component-based frontend applications.
Analytical / Application Questions- A list of editable records uses array indexes as keys. Analyze the likely consequences when records are sorted and propose a correction.
- Design the component architecture for a checkout form containing address fields, payment selection, validation messages, and a confirmation dialog. Identify state ownership, props, slots or children, and events.
- A reusable dialog has twelve Boolean props controlling layout and behavior. Evaluate this design and propose a composition-based alternative.
Multiple Choice Questions with AnswersQuestion 1Which statement best describes JSX? A. A database query language used by React B. JavaScript syntax for describing interface elements C. A CSS preprocessor required by Vue D. A browser API for handling events Correct Answer: B. JavaScript syntax for describing interface elements Question 2What is the preferred source for a key when rendering a list of database records? A. The current array index B. A random number generated during rendering C. A stable unique identifier for the record D. The record's display color Correct Answer: C. A stable unique identifier for the record Question 3In React, what does the children prop normally contain? A. The component's private state B. Content placed between the component's tags C. The browser event queue D. The component's CSS rules Correct Answer: B. Content placed between the component's tags Question 4Which Vue syntax binds an element attribute to an expression? A. @ B. # C. : D. % Correct Answer: C. : Question 5What is the main purpose of a child-emitted event? A. To communicate an occurrence to a parent B. To mutate a parent prop directly C. To replace all component styles D. To create a database table Correct Answer: A. To communicate an occurrence to a parent Question 6Which approach best supports one-way data flow? A. The child directly changes the parent's prop B. The parent supplies data and the child emits an event for requested changes C. Every component writes to the same global object D. The child copies every prop into unrelated state Correct Answer: B. The parent supplies data and the child emits an event for requested changes Question 7Which Vue directive is generally appropriate when content should be removed and recreated according to a condition? A. v-if B. v-show C. v-for D. v-bind Correct Answer: A. v-if Question 8What is a major risk of using array indexes as keys when items can be reordered? A. The application cannot parse JSX B. Component and DOM state may become associated with the wrong item C. CSS files stop loading D. Events are automatically emitted twice Correct Answer: B. Component and DOM state may become associated with the wrong item Question 9What does composition primarily mean in component design? A. Combining smaller components to build larger interfaces B. Converting all CSS into inline styles C. Replacing semantic HTML with generic containers D. Using inheritance for every visual variation Correct Answer: A. Combining smaller components to build larger interfaces Question 10Which practice improves component accessibility? A. Using a clickable div for every action B. Removing visible focus indicators C. Using semantic controls and providing appropriate labels D. Communicating errors through color alone Correct Answer: C. Using semantic controls and providing appropriate labels
|