javascript-today

JSX Basics

JSX - JavaScript XML

JSX is a syntax extension for JavaScript that lets you write HTML-like code in your JavaScript files. It’s one of React’s most distinctive features.

What is JSX?

JSX looks like HTML but it’s actually JavaScript:

const element = <h1>Hello, world!</h1>;

This is neither a string nor HTML - it’s JSX! Babel compiles it to JavaScript:

// What you write
const element = <h1>Hello, world!</h1>;

// What it becomes
const element = React.createElement('h1', null, 'Hello, world!');

Why JSX?

1. Familiar syntax - Looks like HTML you already know
2. Visual - Easy to see the UI structure
3. Powerful - Full JavaScript inside your markup
4. Type-safe - Errors caught at compile time

Basic JSX Syntax

Embedding Expressions:

Use curly braces {} for JavaScript expressions:

const name = "Alice";
const element = <h1>Hello, {name}!</h1>;

const a = 5;
const b = 10;
const math = <p>Sum: {a + b}</p>;

const user = { firstName: "Bob", lastName: "Smith" };
const fullName = <h2>{user.firstName} {user.lastName}</h2>;

JSX is an expression:

You can use JSX anywhere you’d use a JavaScript expression:

// Assign to variable
const greeting = <h1>Hello!</h1>;

// Return from function
function getGreeting() {
  return <h1>Hello!</h1>;
}

// Use in conditions
const element = isLoggedIn ? <UserGreeting /> : <GuestGreeting />;

// Store in array
const elements = [<div>First</div>, <div>Second</div>];

JSX Attributes

Attributes use camelCase:

// HTML
<div class="container" tabindex="0">

// JSX
<div className="container" tabIndex="0">

Common attribute differences:

HTML JSX
class className
for htmlFor
tabindex tabIndex
onclick onClick

Dynamic attributes:

const altText = "Profile picture";
const imgUrl = "/images/avatar.jpg";

const image = <img src={imgUrl} alt={altText} />;

// Conditional classes
const buttonClass = isActive ? "btn-active" : "btn-inactive";
const button = <button className={buttonClass}>Click</button>;

Children in JSX

Text children:

<h1>Hello, world!</h1>

Element children:

<div>
  <h1>Title</h1>
  <p>Paragraph</p>
</div>

Expression children:

<div>
  {user.name}
  {2 + 2}
  {getMessage()}
</div>

Mixed children:

<p>
  Hello, {user.name}! You have {messageCount} messages.
</p>

Single Root Element

JSX must have one parent element:

// ❌ Wrong - multiple roots
return (
  <h1>Title</h1>
  <p>Text</p>
);

// ✅ Right - wrapped in div
return (
  <div>
    <h1>Title</h1>
    <p>Text</p>
  </div>
);

// ✅ Right - using Fragment
return (
  <>
    <h1>Title</h1>
    <p>Text</p>
  </>
);

// ✅ Right - explicit Fragment
return (
  <React.Fragment>
    <h1>Title</h1>
    <p>Text</p>
  </React.Fragment>
);

Fragments (<>...</>) let you group elements without adding extra DOM nodes.

JavaScript in JSX

Any JavaScript expression works:

// Variables
<div>{userName}</div>

// Math
<div>{price * quantity}</div>

// Function calls
<div>{formatDate(new Date())}</div>

// Ternary operators
<div>{isOnline ? "Online" : "Offline"}</div>

// Logical AND
<div>{isLoggedIn && <UserMenu />}</div>

// Array methods
<ul>
  {items.map(item => <li>{item}</li>)}
</ul>

Statements don’t work:

// ❌ Wrong - can't use if statements
<div>
  {if (isLoggedIn) { return "Welcome" }}
</div>

// ✅ Right - use ternary
<div>
  {isLoggedIn ? "Welcome" : "Please log in"}
</div>

// ✅ Right - use function
<div>
  {getGreeting(isLoggedIn)}
</div>

Conditional Rendering

1. Ternary operator (if/else):

function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <h1>Welcome back!</h1>
      ) : (
        <h1>Please sign in</h1>
      )}
    </div>
  );
}

2. Logical AND (if only):

function Mailbox({ unreadMessages }) {
  return (
    <div>
      <h1>Your Mailbox</h1>
      {unreadMessages.length > 0 && (
        <p>You have {unreadMessages.length} unread messages</p>
      )}
    </div>
  );
}

3. Variable assignment:

function Greeting({ isLoggedIn }) {
  let message;
  
  if (isLoggedIn) {
    message = <h1>Welcome back!</h1>;
  } else {
    message = <h1>Please sign in</h1>;
  }
  
  return <div>{message}</div>;
}

Rendering Lists

Use .map() to render arrays:

function TodoList() {
  const todos = [
    { id: 1, text: 'Learn React' },
    { id: 2, text: 'Build app' },
    { id: 3, text: 'Deploy' }
  ];
  
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

Keys are important:

// ✅ Good - unique, stable ID
{items.map(item => <div key={item.id}>{item.name}</div>)}

// ⚠️ OK for static lists only
{items.map((item, index) => <div key={index}>{item}</div>)}

// ❌ Bad - will cause warnings
{items.map(item => <div>{item}</div>)}

Inline Styles

Styles are objects with camelCase properties:

const divStyle = {
  color: 'blue',
  backgroundColor: 'lightgray',
  padding: '10px',
  borderRadius: '5px'
};

<div style={divStyle}>Styled content</div>

// Or inline
<div style={{ color: 'red', fontSize: '20px' }}>
  Red text
</div>

Note: CSS property names use camelCase in JSX:

  • background-colorbackgroundColor
  • font-sizefontSize
  • border-radiusborderRadius

Comments in JSX

return (
  <div>
    {/* This is a comment */}
    <h1>Title</h1>
    
    {/* 
      Multi-line comment
      in JSX
    */}
    <p>Text</p>
  </div>
);

Self-Closing Tags

Tags with no children must be self-closing:

// ❌ Wrong
<img src="photo.jpg">
<br>
<input type="text">

// ✅ Right
<img src="photo.jpg" />
<br />
<input type="text" />

Preventing Injection Attacks

JSX escapes values by default (safe from XSS):

const userInput = '<script>alert("hacked")</script>';

// Safe - renders as text, not executed
<div>{userInput}</div>
// Shows: <script>alert("hacked")</script>

To render HTML (dangerous!), use dangerouslySetInnerHTML:

// Only use with trusted content!
<div dangerouslySetInnerHTML={{ __html: trustedHTML }} />

Practical Examples

Example 1: User Card

function UserCard({ user }) {
  return (
    <div className="user-card">
      <img 
        src={user.avatar} 
        alt={`${user.name}'s avatar`}
        className="avatar"
      />
      <h3>{user.name}</h3>
      <p className="email">{user.email}</p>
      {user.isVerified && (
        <span className="badge"> Verified</span>
      )}
    </div>
  );
}

Example 2: Product List

function ProductList({ products }) {
  return (
    <div className="product-list">
      {products.length === 0 ? (
        <p>No products found</p>
      ) : (
        <div className="grid">
          {products.map(product => (
            <div key={product.id} className="product-card">
              <img src={product.image} alt={product.name} />
              <h4>{product.name}</h4>
              <p className="price">${product.price}</p>
              {product.inStock ? (
                <button>Add to Cart</button>
              ) : (
                <p className="out-of-stock">Out of Stock</p>
              )}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Example 3: Status Indicator

function StatusIndicator({ status }) {
  const statusConfig = {
    online: { color: 'green', text: 'Online' },
    away: { color: 'yellow', text: 'Away' },
    offline: { color: 'gray', text: 'Offline' }
  };
  
  const config = statusConfig[status];
  
  return (
    <div className="status">
      <span 
        className="dot"
        style={{ backgroundColor: config.color }}
      />
      <span>{config.text}</span>
    </div>
  );
}

JSX Best Practices

1. Keep it readable:

// Good - readable
return (
  <div>
    <Header />
    <Content />
    <Footer />
  </div>
);

// Bad - hard to read
return <div><Header /><Content /><Footer /></div>;

2. Extract complex JSX:

function UserProfile({ user }) {
  const profileHeader = (
    <div className="header">
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
    </div>
  );
  
  return (
    <div className="profile">
      {profileHeader}
      <UserDetails user={user} />
    </div>
  );
}

3. Use meaningful keys:

// Good
{users.map(user => <User key={user.id} {...user} />)}

// Bad
{users.map((user, i) => <User key={i} {...user} />)}

Common Mistakes

1. Forgetting curly braces:

// Wrong
<div>userName</div>  // Shows "userName"

// Right
<div>{userName}</div>  // Shows the value

2. Using class instead of className:

// Wrong
<div class="container">

// Right
<div className="container">

3. Missing keys in lists:

// Wrong - console warning
{items.map(item => <div>{item}</div>)}

// Right
{items.map(item => <div key={item.id}>{item}</div>)}