Must knowReact Fundamentals · 04

Custom Components

A component is a function React calls for you. The recorded call order proves it, and why capital letters and props matter.

On this page · 10 sections
  1. In one minute
  2. The example: Price and ProductCard
  3. Calling it yourself vs letting React call it
  4. Every render calls the function again
  5. Capital letters decide what a tag means
  6. Props
  7. Common mistakes
  8. Interview Q&A
  9. Related
  10. Sources

In one minute

A component is a function that takes one object, props, and returns something React can render. That’s the whole definition. The important part is who calls it: when you write <Price cents={1800} />, you don’t call Price; you create an element whose type is the function, and React calls it later, while rendering. Because React owns the call, it can run it again on every render, skip it, or give it state.

You’ll be able to: explain the difference between Price(props) and <Price />, why component names are capitalized, and why a local variable resets on every render.

You call Price()
main.tsx
1function Price({ cents }) {2  log('  Price runs')3  return <p>{formatUSD(cents)}</p>4}56log('1. building elements')7const element = <div>{Price({ cents: 1800 })}</div>8log('2. elements built, calling render()')9reactRoot.render(element)
Console (recorded)
1. building elements
Price runs
2. elements built, calling render()
3. on screen
  1. log('1. building elements')The first log line.
  2. const element = <div>{Price({ cents: 1800 })}</div>You call Price, like any function: it runs right away, while the JSX is still being built.
  3. log('2. elements built, calling render()')Only then is the element finished.
  4. reactRoot.render(element)React receives a <p> element and never sees Price, so it can’t give it state, skip it, or call it again later.

0 / 0
Same output, different owner. The order of the log lines was recorded from the product store.

The example: Price and ProductCard

function Price({ cents }: { cents: number }) {
	log('  Price runs')
	return <p className="price">{formatUSD(cents)}</p>
}
function ProductCard({ product }: { product: Product }) {
	let views = 0 // a plain local: starts at 0 on every call
	views++
	log(`  ProductCard runs (views = ${views})`)
	return (
		<article className="product-card">
			<h2>{product.name}</h2>
			<Price cents={product.priceCents} />
		</article>
	)
}

Calling it yourself vs letting React call it

Calling the function directly:

log('1. building elements')
const element = <div>{Price({ cents: product.priceCents })}</div>
log('2. elements built, calling render()')
reactRoot.render(element)
1. building elements
  Price runs
2. elements built, calling render()
3. on screen

Passing the function to createElement (what <Price cents={…} /> compiles to):

log('1. building elements')
const element = <div>{createElement(Price, { cents: product.priceCents })}</div>
log('2. elements built, calling render()')
reactRoot.render(element)
1. building elements
2. elements built, calling render()
  Price runs
3. on screen

Same screen, different order. In the first, Price runs while the elements are being built, and React only ever receives the <p> it returned. In the second, Price runs after render(), inside React. Only the second version is a component as far as React is concerned: it can have state and effects, React can skip it when nothing changed, and DevTools shows it.

Every render calls the function again

The lesson renders <ProductCard /> three times:

render #1
  ProductCard runs (views = 1)
  Price runs
render #2
  ProductCard runs (views = 1)
  Price runs
render #3
  ProductCard runs (views = 1)
  Price runs

views is 1 every time. A component function runs from the top on each render, so plain local variables start over. Remembering something between renders is exactly what useState and useRef are for (the Hooks module).

Capital letters decide what a tag means

The compiler looks only at the tag’s first letter:

You write Compiles to React treats it as
<price /> jsx("price", {}) a DOM element called price (doesn’t exist; React warns)
<Price /> jsx(Price, {}) your function
<ui.Price /> jsx(ui.Price, {}) your function (member access works too)

Because type is just a value, you can choose a component dynamically: const Card = isCompact ? CompactCard : ProductCard and then <Card />.

Props

Props are whatever the author decides: strings, numbers, objects, functions, other elements. children isn’t special either; JSX just gives it a nicer syntax: <Tag>New</Tag> is the same as <Tag children="New" />.

Common mistakes

Interview Q&A

What is a React component, really?

A function that accepts a props object and returns something renderable (elements, a string, a number, null, an array). No class or special syntax is required. The one rule is that React, not you, decides when it runs.

What's the difference between Price(props) and <Price {...props} />?

Price(props) is an ordinary call: it runs immediately and React only gets its return value. <Price /> creates an element { type: Price, props }; React calls Price later while rendering. The recorded logs show Price runs before “elements built” in the first case and after render() in the second. Only the element form can use hooks, be skipped, or appear in DevTools.

Why must component names start with a capital letter?

The JSX compiler compiles lowercase tags to strings (DOM elements) and capitalized or dotted tags to variable references. <price /> becomes jsx("price"), a non-existent DOM element.

Is children special?

No. It’s an ordinary prop with extra syntax: whatever is between the tags becomes props.children.

Does a component keep its local variables between renders?

No. The function runs from the top on every render; the recording shows views = 1 on all three renders. Values that must survive go in state (useState) or a ref (useRef).

Sources

Primary source: react.dev/learn/your-first-component