Course outline

React / Components and state / Lesson 2

JSX, expressions, and component inputs

Translate interface structure into JSX and define props that make component behavior explicit.

About 4 min readContent reviewed
On this page

Lesson goals

  • JSX is source syntax, not an HTML string
  • Render text values safely
  • Check interfaces at their boundaries

JSX is source syntax, not an HTML string

JSX embeds element descriptions in JavaScript source and is transformed by the project’s build configuration. It is not parsed as an ordinary HTML string at runtime. Close tags, return one enclosing expression or a Fragment, and use JavaScript property conventions such as className and htmlFor where React DOM expects them. Curly braces insert an expression, not an arbitrary statement block. Keep calculations readable by naming intermediate values instead of nesting many conditions inside markup. The resulting elements still need valid HTML relationships; JSX successfully compiling does not prove a paragraph can contain every block element.

Props are read-only inputs

A parent supplies props to a component, which reads them for that render. Destructuring makes the contract visible, and a default parameter can cover an omitted or undefined value. null is a deliberate value and does not trigger that default automatically. Do not mutate a prop object to “correct” it inside a child; validate or normalize at an appropriate boundary and communicate changes through callbacks owned by the parent. A prop name should describe meaning and units where relevant. A count of seats and a price in cents may both be numbers but are not interchangeable contracts.

Render text values safely

The first example places a name prop into a heading expression. React DOM treats the resulting string as text rather than interpreting markup inside it as HTML. This default is valuable, but it is not a complete application security boundary: URLs, raw HTML escape hatches, server actions, and authorization still need deliberate validation. Never switch to raw HTML simply because an input contains angle brackets. Use structured components for supported formatting. The output in this example is ordinary text, and the component does not need to know where the trusted synthetic name originally came from.

jsx Component module — requires the course React host
function Greeting({ name = "Guest" }) { return <h1>Hello, {name}</h1>; }
export default function App() { return <main><Greeting name="Mira" /></main>; }
Expected output
Hello, Mira

A named prop becomes a text child. The default is available for undefined input but is not used here.

Use children for composition slots

The children prop lets a parent provide nested React content without making a wrapper understand every possible inner structure. The second example defines a labeled panel with a title prop and a content slot. This is composition, not permission to mutate or inspect arbitrary children to infer hidden business rules. Keep the wrapper’s semantics appropriate for its contents, and avoid automatically assigning the same heading level in every possible page context without a documented API. A component can accept a deliberate heading choice when reuse truly requires it. Prefer a small understandable contract over many flags controlling unrelated markup variants.

jsx Component module — requires the course React host
function Panel({ title, children }) { return <section><h2>{title}</h2>{children}</section>; }
export default function App() { return <main><h1>Workshop</h1><Panel title="Materials"><p>Bring local notes.</p></Panel></main>; }
Expected output
Workshop
Materials
Bring local notes.

The parent owns the content and the wrapper owns a simple section structure.

Check interfaces at their boundaries

A component API should explain required inputs, optional defaults, accepted children, and callback expectations. Static typing or linting can help but does not validate untrusted runtime data by itself. Inspect empty strings, omitted values, null where permitted, longer translations, and child content that wraps. Keep accessibility relationships explicit: a label’s htmlFor still needs a matching unique id, and a styled wrapper does not invent a landmark. Review the compiled behavior in the host project after JSX syntax checks. A clean build establishes that the source can be processed, not that the interface communicates the right information.

Common errors and fixes

A prop default does not replace null

Why it happens
Default destructuring applies to undefined.
How to fix it
Handle null explicitly when the contract permits it.
Prevent it next time
Test omitted, undefined, and null separately.

JSX rejects a statement inside braces

Why it happens
An expression slot contains control-flow statements.
How to fix it
Compute the value before returning JSX.
Prevent it next time
Keep complex logic named and testable.

A child changes its parent’s data

Why it happens
It mutated a prop object.
How to fix it
Treat inputs as read-only and request changes through callbacks.
Prevent it next time
Document ownership and use immutable updates.

Check your understanding

1. Design a price component API that avoids ambiguous numeric inputs.

Show worked solution

Use a prop such as amountCents and an explicit currency or formatting policy. Validate the source data before presentation and keep the component read-only. A bare value prop leaves units unclear, while mutating it in the child can corrupt shared state.

2. When should a wrapper accept children rather than a long text-only configuration object?

Show worked solution

Use children when the parent should compose meaningful nested content and the wrapper only supplies a stable boundary. Keep explicit props for values the wrapper genuinely needs, such as its title. This separates ownership without making the wrapper parse another miniature markup language.

Key takeaways

  • JSX expressions produce descriptions, not raw HTML strings.
  • Props are read-only render inputs.
  • Composition needs an explicit semantic contract.

Official references