Blog

Part 2 · React Elements and Components: What Is the Difference?

A component is a function you write. An element is the object JSX makes. Learn who calls your component and when, why logs appear twice in development, and three mistakes that come from mixing the two up.

In Part 1 you saw that JSX makes plain objects called elements. This part adds the second big word in React: component.

The two words are easy to mix up. Knowing the difference explains some strange things React does, like why a log can appear twice.

Try this first

Read this code. Don’t press Run yet.

function Greeting({ name }: { name: string }) {
  console.log('Greeting runs for', name)
  return <p>Hello, {name}!</p>
}

export default function App() {
  const card = <Greeting name="Ana" />
  console.log('App made an element')

  return (
    <div>
      {card}
      <Greeting name="Ben" />
    </div>
  )
}
App made an element
App made an element
Greeting runs for Ana
Greeting runs for Ana
Greeting runs for Ben
Greeting runs for Ben

Greeting({ name }: { name: string }) means “Greeting gets a name, and the name is text”. The { name } part takes name out of the object React passes in. Values passed to a component like this are called props. Part 3 is all about them.

Now make a guess. The line const card = <Greeting name="Ana" /> comes before console.log('App made an element'). So which line will the console show first: “Greeting runs for Ana” or “App made an element”?

Press Run, and look at the Console under the result.

“App made an element” comes first. When App wrote <Greeting name="Ana" />, the Greeting function did not run. It ran later.

You probably noticed something else too. Every line appears twice. We’ll explain that soon. First, the two words.

A component is a function you write

A component is a function that returns JSX. Greeting and App above are both components.

A function becomes a component when you use it as a tag, like <Greeting />. Then React calls it for you. Its name must start with a capital letter, so that JSX knows it is yours and not an HTML tag. And it must return something React can show, usually JSX.

You write a component once. Then you use it like a tag, as many times as you want: <Greeting name="Ana" />, <Greeting name="Ben" />.

An element is the object JSX makes

An element is the plain object that a JSX tag turns into. You met it in Part 1. For <Greeting name="Ana" />, the element looks like this:

{ type: Greeting, props: { name: "Ana" } }

type is the Greeting function itself, not its name as text. props holds what you passed to it.

An element is only a description. It says “I want a Greeting here, with these props”. It doesn’t run anything.

Run this to see the difference:

function Greeting() {
  return <p>Hello!</p>
}

export default function App() {
  const element = <Greeting />

  return (
    <ul>
      <li>typeof Greeting: {typeof Greeting}</li>
      <li>typeof element: {typeof element}</li>
      <li>element.type is Greeting: {String(element.type === Greeting)}</li>
    </ul>
  )
}

Greeting is a function. <Greeting /> is an object. And the object’s type points back at the function.

Who calls the component?

You never call Greeting() yourself. React does. Here is the order of events.

1. You write JSX with a capital letter: a component. 2. That makes an element. Greeting has not run yet. 3. React renders the element. Now React calls your function. 4. Your function returns more elements. 5. When rendering is done, React puts a real <p> on the page. <Greeting name="Ana" /> { type: Greeting, props: { name: "Ana" } } Greeting({ name: "Ana" }) { type: "p", props: { children: ["Hello, ", "Ana", "!"] } } Hello, Ana!the browser page now has <p>Hello, Ana!</p>

Who calls the component? React does, while it renders. Press play, or step through it.

  1. You write <Greeting name="Ana" />.
  2. That makes an element: { type: Greeting, props: { name: "Ana" } }. Greeting has not run yet.
  3. React starts to render, which means working out what the page should show. It sees that type is a function, so it calls Greeting({ name: "Ana" }).
  4. Greeting returns more elements: { type: "p", props: { children: ["Hello, ", "Ana", "!"] } }. The text has three parts, because {name} sits in the middle of it.
  5. Now type is the text "p", an HTML tag. When rendering is done, React puts a real <p> on the page.

React keeps doing this until only HTML tags are left. Then it knows exactly what the page should look like.

We tested steps 2 and 3 with React 19.3, with Strict Mode off. Making the element called Greeting 0 times. Rendering it called Greeting once.

An everyday example

A component is a recipe. “Cake: mix flour, sugar and eggs, then put it in the oven.”

An element is an order note. “One cake, for Ana.”

Writing the note doesn’t make a cake. You hand the note to the cook, and the cook decides when to follow the recipe. React is the cook.

The exact version

Elements are cheap. They are small objects, and making one doesn’t run your function. Rendering costs more, because that is when React calls your components. It is usually still fast.

This is also why React must be the one to call them. React decides when each component runs and in what order. It also connects each run to the right place on the page. That place is what lets a component remember things between runs. What a component remembers is called its state. Part 4 is about state.

Why is every line logged twice?

Look back at the console from “Try this first”. App and Greeting each ran twice.

That is React’s Strict Mode. Vite is a popular tool for starting React projects. A new React project made with Vite turns Strict Mode on, with a <StrictMode> tag around the whole app. The playground does the same, so it behaves like a real project.

In Strict Mode, React calls each component twice while you develop. It does this to find bugs. A component should give the same result every time it runs with the same inputs. If running it twice changes something, your component has a bug, and the second run makes it easier to see.

This happens only in development. React’s docs say all of Strict Mode’s checks are “development-only and do not impact the production build”. When you build your app for real users, each component runs once each time React renders it.

So when you see double logs, don’t worry. It is React checking your work.

One element, used twice

In “Try this first”, the card element was used once. What happens if you use the same element object in two places?

function Greeting({ name }: { name: string }) {
  return <p>Hello, {name}!</p>
}

export default function App() {
  const card = <Greeting name="Ana" />

  return (
    <div>
      {card}
      {card}
    </div>
  )
}

Run it. “Hello, Ana!” shows twice.

That makes sense once you know what an element is. It is a description, like an order note. You can hand the same note to the cook twice and get two plates. We counted: with Strict Mode off, React called Greeting 2 times for this code, once for each place on the page.

Why it matters: the same element can save work

When a component runs again, we say it re-renders. React gets new elements from it. Usually they are brand new objects. But one of the elements inside might be the exact same object as last time. Then React knows that element’s props did not change. If that child component has nothing new of its own to show, React can skip calling it again.

We measured this with React 19.3. A parent component re-rendered 3 times. We counted how many times its child component ran, not counting the first time the page loaded.

How the parent shows the child Child ran (Strict Mode off) Child ran (Strict Mode on)
Writes <Child /> inside itself, so a new element every time 3 6
Gets the element from outside, as children 0 0

“Nothing new of its own” matters. A child can still re-render when its own state changes, or when something it reads from context changes. Later parts cover both.

children is a special prop that holds whatever you put between a component’s tags. Part 3 explains it.

You can watch it happen. This example uses a few things that later parts explain properly:

  • useState lets a component remember a value between runs. Part 4 explains it. Here, all that matters is that clicking the button makes Parent run again.
  • onClick={() => setClicks(clicks + 1)} gives the button a small function to run when it is clicked. Part 6 covers events.
  • type ReactNode brings in the type for “anything React can show”, so TypeScript knows what children holds.
import { useState, type ReactNode } from 'react'

function Child() {
  console.log('Child runs')
  return <p>I am the child.</p>
}

function Parent({ children }: { children: ReactNode }) {
  const [clicks, setClicks] = useState(0)
  console.log('Parent runs')
  return (
    <div>
      <button onClick={() => setClicks(clicks + 1)}>Clicks: {clicks}</button>
      {children}
    </div>
  )
}

export default function App() {
  return (
    <Parent>
      <Child />
    </Parent>
  )
}
Parent runs
Parent runs
Child runs
Child runs
Parent runs
Parent runs

Press Run, then click the button a few times. Each click logs “Parent runs” twice, because of Strict Mode. But “Child runs” never appears again.

App made the <Child /> element and passed it to Parent as children. A click makes only Parent run again, not App. So children is still the same object, and React skips Child.

Now press Edit. Delete {children} and write <Child /> in its place. Run it and click again. This time “Child runs” appears twice on every click: once for the click, and once more because of Strict Mode. Parent now makes a new element every time.

You don’t need to plan for this yet. Most of the time the extra runs are fast. But it is the idea behind one of React’s main ways to make an app faster. We’ll come back to it in Part 19, on composition.

Common mistakes

Calling a component like a function

You might try writing {Counter()} instead of <Counter />. It can even seem to work. But when you call Counter() yourself, React doesn’t know Counter is a component. To React, the code inside Counter is just part of the component that called it.

That breaks when the call happens only sometimes:

import { useState } from 'react'

function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>Count: {count}</button>
}

export default function App() {
  const [show, setShow] = useState(false)
  return (
    <div>
      <button onClick={() => setShow(!show)}>Show the counter</button>
      {show && Counter()}
    </div>
  )
}

{show && Counter()} means “if show is true, call Counter()“. !show means “the opposite of show“. Part 7 explains both.

Run it and press “Show the counter”. React stops with the error “Rendered more hooks than during the previous render”.

Hooks are React functions whose names start with use, like useState. React expects each component to call the same hooks, in the same order, every time it runs. Here useState from Counter counts as part of App. So after the click, when App runs again, it suddenly calls one more hook than before.

The fix: change {show && Counter()} to {show && <Counter />}. Now Counter is its own component, with its own hooks, and React manages it. Try it.

Making a component inside another component

This looks fine:

import { useState } from 'react'

export default function App() {
  const [parentClicks, setParentClicks] = useState(0)

  function Box() {
    const [boxClicks, setBoxClicks] = useState(0)
    return <button onClick={() => setBoxClicks(boxClicks + 1)}>Box: {boxClicks}</button>
  }

  return (
    <div>
      <button onClick={() => setParentClicks(parentClicks + 1)}>App: {parentClicks}</button>
      <Box />
    </div>
  )
}

Run it. Click “Box” twice, so it shows 2. Then click “App”.

The box goes back to 0.

Each time App runs, it makes a brand new Box function. The element <Box /> now has a different type from last time. So React thinks it is a different component. It removes the old box from the page, with everything it remembered, and adds a new one. We checked: after the parent re-rendered, the box was a new button on the page, not the old one.

The fix: move function Box() out of App, to the top level of the file. React’s docs say it plainly: “Never define a component inside another component!”

Starting a component’s name with a small letter

function greeting() {
  return <p>Hello!</p>
}

export default function App() {
  return <greeting />
}

As you saw in Part 1, a small first letter means an HTML tag. <greeting /> becomes the text "greeting". So React looks for an HTML tag called greeting, and never calls your function. In an editor that checks TypeScript, like VS Code, you see an error at once: greeting is not one of the HTML tags TypeScript knows. The playground doesn’t check types. But press Run and React warns you in the Console. Its message ends with start its name with an uppercase letter. Name it Greeting.

Practice

  1. In “Try this first”, add console.log('Greeting is', typeof Greeting) at the top of App. What does it print, and why is it not “Greeting runs”?
  2. In the “same element, used twice” example, add console.log('Greeting runs') as the first line of Greeting. Then make App show {card} three times. How many lines do you think the Console shows?
  3. Fix the “Calling a component like a function” example, and check that the counter works.
  4. Fix the “inside another component” example by moving Box out of App. Click “Box” twice and then “App”. Does the box keep its number now?
Answers
  1. It prints “Greeting is function”, twice because of Strict Mode. Reading the name Greeting doesn’t call the function. Only React calls it, when it renders a <Greeting /> element.
  2. 6 lines: Greeting runs once for each of the 3 places, and Strict Mode doubles each one.
  3. With {show && <Counter />}, the button appears, and clicking it counts up.
  4. Yes. The box keeps showing 2 after you click “App”, because Box is now the same function every time.

Interview questions

Try to answer each one out loud before you open the answer.

What is the difference between a React element and a component?

A component is a function you write. It takes props and returns elements. An element is a plain object that describes what to show, and you must not change it. It has a type and props, and sometimes a key. JSX makes elements. <Greeting /> is an element whose type is the Greeting component. Elements are cheap to make. Rendering, when React calls the components, is where the work happens.

A strong answer adds a third idea. Each time an element is shown somewhere on the page, React keeps track of that place. That is where the component’s state lives.

Does writing <Greeting /> call the Greeting function?

No. It only makes an element, { type: Greeting, props: {} }. React calls Greeting later, when it renders that element. A good answer proves it. Put a console.log in the component and another after the JSX line. The second one prints first.

Why shouldn’t you call a component as a plain function, like {Counter()}?

Then React doesn’t treat it as a component. Its hooks become part of the parent. The call might happen only sometimes, or in a loop that changes length. Then the parent calls a different number of hooks on different runs. React throws “Rendered more hooks than during the previous render”. React’s docs list “Never call component functions directly” as a rule. When React calls components, it can give each one its own state. It can also decide when and in what order they run.

Why does my component render twice in development?

Strict Mode. In development, React calls components twice to find code that changes something while rendering. That kind of code is a bug, and running it twice makes the bug easier to see. React’s docs say Strict Mode also runs some other code an extra time, such as Effects, which come in Part 11. None of this happens in a production build. A follow-up question is often: “Should you turn Strict Mode off to fix double logs?” The answer is no. Fix the code that depends on running once.

What goes wrong if you define a component inside another component?

Every time the outer component runs, it makes a new function. So the inner element’s type is different each time, and React treats it as a different component. React removes the old one and adds a new one. So the inner component loses its state, and any text a user typed into it. It is also slower, because React builds that part of the page again each time. Define components at the top level and pass data in through props.

How can returning the same element object make React skip work?

Say a component re-renders, and one element it returns is the exact same object as before. Then React knows that element’s props didn’t change. If the child has no update of its own, React can skip calling it. You can get this by passing elements in through children. In our test, the parent re-rendered 3 times. A child passed as children ran 0 times. A child written inside the parent ran 3 times with Strict Mode off, and 6 with it on.

A strong answer names the limits. The child still re-renders if its own state changes, or if a context it reads changes. It also names React.memo, which skips a re-render when the props are equal, even if the element is new. Part 20 covers it.

Can the same element object be used in two places?

Yes. An element is only a description, so React can use it in two places. It renders the component once for each place. Each place gets its own copy on the page, and its own state. In our test, one <Counter /> element shown twice gave two counters. Clicking the first one changed only the first.

Sources

  • Your First Component, react.dev: what a component is, capital letters, and “Never define a component inside another component!”
  • React calls Components and Hooks, react.dev: “Never call component functions directly”, and why React must call them.
  • createElement, react.dev: what an element holds and why it must not be changed.
  • The create-vite 9.2.1 React template’s src/main.tsx, which wraps the app in <StrictMode>.
  • StrictMode, react.dev: why components run twice in development, and that the checks are development-only.
  • Render and Commit, react.dev: what React does when it renders.
  • React Components, Elements, and Instances, React blog, 2015: the original explanation of elements as descriptions.
  • The call counts and the table above come from running React 19.3.0 for this post. The Vite setting comes from the create-vite 9.2.1 React template.
  • This part follows the Element vs Component kata in react-katas.

How useful was this post?

Click on a heart to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.