Writing clean and maintainable code is crucial for long-term project success, especially when working with JSX in React applications. Below are some essential tips and strategies, accompanied by code examples, to help you write better JSX.
Follow me on X
1. Keep Components Small and Focused
Divide your application into small, manageable components that do one thing and do it well. This makes your code easier to understand and test.
// Good
function UserProfile({ user }) {
return (
<div>
<UserAvatar user={user} />
<UserInfo user={user} />
</div>
);
}
// Avoid
function UserProfile({ user }) {
// A large component that handles rendering everything related to the user
}
2. Use Descriptive Component Names
Choose clear and descriptive names for components to indicate what they do or represent.
// Good
function EmailInputField() {
// ...
}
// Avoid
function InputField() {
// ...
}
3. Destructure Props for Clarity
Destructure props in functional components to make it clear which properties the component expects and uses.
// Good
function Greeting({ name, message }) {
return <h1>{`Hello, ${name}! ${message}`}</h1>;
}
// Avoid
function Greeting(props) {
return <h1>{`Hello, ${props.name}! ${props.message}`}</h1>;
}
4. Keep JSX Readable with Proper Indentation
Just like HTML, properly indented JSX is crucial for readability. Use a consistent indentation style.
// Good
return (
<div>
<h1>Title</h1>
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
// Avoid
return <div><h1>Title</h1><ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul></div>;
5. Abstract Conditional Logic
Move complex conditional logic outside of your JSX to keep the template clean.
// Good
function WelcomeBanner({ user }) {
const isLoggedIn = user != null;
return (
<div>
{isLoggedIn ? <LoggedInBanner user={user} /> : <LoggedOutBanner />}
</div>
);
}
// Avoid
function WelcomeBanner({ user }) {
return (
<div>
{user != null ? <LoggedInBanner user={user} /> : <LoggedOutBanner />}
</div>
);
}
6. Use Comments Wisely
Inline comments can clarify the purpose of complex parts of the template, but avoid over-commenting obvious things.
// Good
function ComplexComponent() {
// Fetching data from the API and filtering for active users
// ...
return (
// Render only if there are active users
{activeUsers.length > 0 && (
<UserList users={activeUsers} />
)}
);
}
// Avoid
// This is a div
// ...
7. Avoid Inline Styles
Prefer external stylesheets or styled-components over inline styles for better separation of concerns and reusability.
// Good
import "./Button.css";
function Button({ label }) {
return <button className="primary-button">{label}</button>;
}
// Avoid
function Button({ label }) {
return <button style={{ backgroundColor: 'blue', color: 'white' }}>{label}</button>;
}
8. Prop Types and Default Props
Use propTypes
for type-checking and defaultProps
to define default values for props.
import PropTypes from 'prop-types';
function UserProfile({ name, age }) {
// ...
}
UserProfile.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
};
UserProfile.defaultProps = {
age: 30,
};
9. Functional Over Class Components
Whenever possible, use functional components with hooks. They are more concise and easier to test.
// Good
function App() {
const [count, setCount] = useState(0);
// ...
}
// Avoid
class App extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
// ...
}
// ...
}
10. Use Fragment Shorthand
Use the <>...</>
shorthand for fragments to avoid additional clutter in your JSX.
// Good
function Group({ children }) {
return <>{children}</>;
}
// Avoid
function Group({ children }) {
return <React.Fragment>{children}</React.Fragment>;
}
By adhering to these guidelines,
Top comments (14)
Adding to #7: Since we use Tailwind for CSS and the
className
string can get quite long, I started abstracting it out to aclasses
object to keep the JSX code more concise. For example:This is an overly complex solution for providing semantic class names that only does half the job. If you want to abstract your classnames like this, you should just use Tailwind's CSS directives:
Now you don't have to pass around that utility object:
You also have the added benefit of having semantic classes in your application markup.
Using a utility object:
Using a tailwind directives:
That will make it much easier for non-developers (for example, customer support team) to identify specific components in your markup, making issues much clearer.
Thank you for the thorough reply! I wasn't previously familiar with the
@apply
directive.Definitely a nice option & something I'd consider using in the future.
OMG. Why not just use CSS in the first place?
Well it's still CSS, just based on utility classes.
It's certainly a matter of personal preference, but for me I think it's a logical approach to styling when using React or other component-based libraries.
What would've previously been a CSS class (e.g.
<div class=".calendar-wrapper"
) would now be a component (e.g.<CalendarWrapper />
., so the CSS classes are mostly for consistency of UI & branding rather than reusing component styles.Also, passing down a
className
prop to components allows for inheritance-based style customization that would've previously been done with class chaining:as compared to something like:
Because some people prefer tailwind over pure CSS
Which never made sense to me. Why learn and use a new non-standard syntax to do the same thing in a less optimal way (especially if you have to extract it to a separate file again)? But well, I guess do not have to understand that.
Because you can do it on demand.
With tailwind you have compactness. You can style components inside a single file, without the styles taking more visual space than the ts/html/jsx part.
Tailwinds only real alternative is inline css or css-in-jss.
All other non-tailwind solutions are much longer / more code (and mostly used in a separate file because it's so much) .
In terms of readability
i would prefer this one:
2. Use Descriptive Component Names
I would argue that both of these are descriptive names and I would expect that
InputField
is a generic input component that will be used to create specific inputs:3. Destructure Props for Clarity
I think this is definitely true for JavaScript users. But with TypeScript, I'd recommend the opposite a lot of the time. Also, you should just pass props if any of your input is stateful so you don't end up with a bunch of duplicated nomenclature:
This is just adding noise and making it complicated to reason about:
5. Abstract Conditional Logic
This isn't is a very good example of complex conditional logic. In the current form, you are just doing a loose falsy comparison on
user
.In this case, you should flip your examples:
This first version just adds unnecessary noise for such a simple comparison. Something so simple should be expressed inline:
This is better, but the loose comparison is unnecessary too. For simple conditions like this the most common pattern I see in the React community is like this:
Short, simple and to the point.
Now say you have an actually complex condition, then you should extract it from your component template:
But that's still bad as you have to reason about it in your component, and this kind of logic may be needed elsewhere.
Better to abstract it out of the component completely:
I think this is one piece of advice that's always missing from these kinds of articles: express component logic in your component, express your application logic elsewhere.
6. Use Comments Wisely
I feel like all of your examples here are bad comments. There's no difference between:
They both literally describe the code. If your API call looks something like this, then it should be pretty clear what's happening. And if it doesn't look like this, you should modify it to make it more explicit.
And this is explicit from the wording. It would be very worrying if developers need a comment to understand what is happening here:
I think in your example, the only comment that's needed is to document the component's purpose as its name is very generic and doesn't describe itself:
A better solution here might be renaming the component, so that the comment is redundant:
And if you find yourself needing to pepper your component with comments, you should probably go back to step one and break it up into smaller components.
7. Avoid Inline Styles
This is a big opinion. Your example is very basic and not really inline with how a lot of teams would use inline styles. At the very least you should update it to use some variables:
But again, your advice here is personal opinion... On the one hand, extracting styles makes components cleaner. On the other hand, now you've increased the complexity of your application and maintenance burden.
Which of these is preferable is a matter for individual teams to determine.
8. Prop Types and Default Props
This is certainly true for JavaScript users. In this case the
prop-types
package is almost mandatory. But I've almost never used it on a TypeScript project. TypeScript is very good at catching a lot of these kinds of errors before runtime which just makesprop-types
redundant.If you need to check types and enforce defaults, just define a default props variable and merge them with passed props:
I would add point 1: use a proper state manager.
Without it, or relying on React build-in defaults like useState, useContext and useEffect to manage state - is pretty much a code smell. (With single exception in writing ui library code and using useContext where)
I recommend Jotai.
Resct number 1 biggest problem is unnecessary re-renders. Absolutely every react developer suffers because of it at least once.
This problem goes completely away using a proper state manager (and using it right).
Full Fragment is useful in for loops where you can group all entries with a react
key
prop without having to add it to every single component"Clean and Maintainable JSX Code" feels very much like an oxymoron
In comparison to that, jquery and direct dom manipulation?
I would say the title could be changed to "clean and maintainable react component code"
because half the suggestions do not apply to Svelte, Solidjs or other Solutions that use JSX as well.
The times where React was the only one using JSX are behind us.