You already have one component. It is the whole of src/App.jsx:
function App() {
return <h1>Hello from React</h1>;
}
export default App;
A function that returns markup. That is the entire definition. The two halves worth
pulling apart are what makes a function a component, and what the markup can do.
Writing a second one
Add a function above App and use it inside:
function Greeting() {
return <p>Nice to meet you.</p>;
}
function App() {
return (
<div>
<h1>Hello from React</h1>
<Greeting />
</div>
);
}
export default App;
Save, and the paragraph appears. Three things just happened.
<Greeting /> is a function call. Not a string, not a template - React sees that tag,
recognises it as one of yours, calls Greeting(), and puts whatever it returns in that
spot. Writing a component and using it are as ordinary as writing a function and calling
it, because that is what they are.
The name has to start with a capital letter. This is a hard rule, not a convention.
Lowercase tags are treated as HTML elements, so <greeting /> asks for an HTML element
called “greeting”. You get an empty <greeting></greeting> in the page and none of your
paragraph. <Greeting /> is capitalised, so React looks for a variable of that name
instead.
React tells you exactly this. Open the browser console and it is there:
The tag <greeting> is unrecognized in this browser.
If you meant to render a React component, start its name with an uppercase letter.
When a component renders nothing, the console usually already knows why. Get in the habit
of having it open. Most of this course’s dead ends are two seconds long if you do and
twenty minutes long if you do not.
App returns one div wrapping both children. Remove it, return the h1 and the
<Greeting /> side by side, and the build fails.
The rules of JSX
JSX is not HTML and it is not a string. It is syntax that the build tool turns into
JavaScript function calls before the browser ever sees it - which is why you needed Vite
in the first place. Because it becomes JavaScript, it inherits some constraints that HTML
does not have.
One root element
A function returns one value, so a component returns one element. These two are the same
rule:
// Fails. Two things, no container.
function App() {
return (
<h1>Hello</h1>
<p>Goodbye</p>
);
}
Adjacent JSX elements must be wrapped in an enclosing tag.
Wrapping them in a <div> fixes it, but that is a real div in the page, and adding one
purely to satisfy a syntax rule litters your HTML. So JSX has an empty tag that groups
children without rendering anything:
function App() {
return (
<>
<h1>Hello</h1>
<p>Goodbye</p>
</>
);
}
That is a fragment. Use it whenever the wrapper would exist only to be a wrapper. Use
a real div when you actually want a div - to style it, for instance.
So the <div> in your own App was the wrong choice: it existed only to hold two
children. Change it to <> and </> now.
Every tag closes
HTML forgives <br>, <img src="…"> and a <li> you never closed. JSX does not. Every
element closes, and elements with no children close themselves:
<br />
<img src="/cat.jpg" alt="A cat" />
<input type="text" />
Forget the slash and you get another error that says the thing:
Expected corresponding JSX closing tag for 'br'.
className, not class
<p className="lede">Hello</p>
Because JSX becomes JavaScript, and class is a reserved word in JavaScript. The
attribute is className, which is also what the DOM property has always been called.
for on a label has the same problem and becomes htmlFor.
Both are worth knowing, because the failure is not obvious. React passes the attribute
through to the DOM anyway - write class="lede" and your styling works - but it logs a
warning while it does it:
Invalid DOM property `class`. Did you mean `className`?
So the code appears to work and the console disagrees. Believe the console.
Everything else is what you already know, with one twist: multi-word attributes are
camel-cased. onclick becomes onClick, tabindex becomes tabIndex, maxlength
becomes maxLength.
Braces let JavaScript back in
This is the part that makes JSX worth having. Anywhere inside JSX, { and } mean “stop
writing markup, evaluate this JavaScript expression, and put the result here”:
function App() {
const name = "Gjorge";
const year = new Date().getFullYear();
return (
<>
<h1>Hello, {name}</h1>
<p>It is {year}.</p>
<p>Next year is {year + 1}.</p>
</>
);
}
Anything that produces a value works: a variable, arithmetic, a function call, a property
lookup, a ternary. What does not work is a statement - if, for and while are not
expressions and cannot go inside braces. That sounds limiting. It is not, for reasons
lesson 5 and lesson 6 are about.
Braces work in attributes too, and this is where the difference between a string and an
expression starts to matter:
function Photos() {
const imageUrl = "/tabby.jpg";
return (
<figure>
{/* quotes give the literal string "/cat.jpg" */}
<img src="/cat.jpg" alt="A cat" />
{/* braces give whatever the variable holds */}
<img src={imageUrl} alt="Another cat" />
{/* the number 40, not the string "40" */}
<input maxLength={40} />
</figure>
);
}
Quotes for a literal string, braces for everything else.
Those {/* … */} are how you comment inside JSX. Markup is not JavaScript, so a comment
has to be smuggled in as an expression like anything else. A bare // between tags ends
up on the page as text.
Two braces means an object
One case trips up nearly everyone. Inline styles take an object, not a string:
<p style={{ color: "crimson", fontSize: 20 }}>Careful.</p>
That is not special syntax. The outer braces are “here comes an expression”, the inner
ones are an object literal, and the two just happen to sit next to each other. Note the
camel case again - fontSize, not font-size, because those are JavaScript property
names now. A number with no unit is treated as pixels.
You will not use this much. Stylesheets are still the better answer for almost everything,
and className is how you reach them. But the double brace looks like a typo the first
time you meet it, and now it does not.
Splitting a page into components
Any piece of JSX can be lifted out into its own function and used by name:
function SiteHeader() {
return (
<header>
<h1>My first React app</h1>
<p>Learning the basics.</p>
</header>
);
}
function Footer() {
return <footer>Built in {new Date().getFullYear()}.</footer>;
}
function App() {
return (
<>
<SiteHeader />
<main>
<p>The interesting part goes here.</p>
</main>
<Footer />
</>
);
}
export default App;
App now reads as an outline of the page. That is the point of components, and it is the
same instinct that makes you break a long function into shorter ones: not because the
computer cares, but because a reader does.
When you split a component into its own file, it is the import and export from the
last lesson and nothing more. Footer.jsx ends with export default Footer, and App.jsx
starts with import Footer from "./Footer.jsx". One component per file is the usual
convention, and there is no rule enforcing it.
The limit you are about to hit
SiteHeader above is stuck. It renders exactly one heading and exactly one paragraph, so
the moment you want a second header with different words you are copying the function and
editing the copy - which is where the whole idea falls apart.
What is missing is a way to hand a component some data when you use it, the way you hand a
function arguments. That is the next lesson.
Recap
A component is a function that returns markup, and using one is calling it. Its name must
be capitalised or React will look for an HTML element instead and quietly render nothing.
JSX is markup that compiles to JavaScript, which is why it needs one root element (or a
<> fragment), closes every tag, spells it className, and lets any JavaScript expression
in through braces.
Next: props - passing data into a component, so the same component can render different
things.