I have spent over years building complex React applications for high-traffic platforms in the USA. One pattern that often confuses developers is returning a component directly from a custom hook.
I remember the first time I tried this while building a mortgage calculator for a real estate firm in New York. I wanted to encapsulate both the math logic and the result display in a single, reusable unit.
In this tutorial, I will show you exactly how to return components from hooks using three different methods.
Return a Component from a Hook
When you build a hook, you usually return state values or functions to update that state.
However, sometimes your logic is so tied to a specific UI that it makes sense to return the UI itself.
I find this incredibly useful for modals, loaders, or complex form fields that need to stay consistent across a site.
Instead of repeating the JSX in every component, the hook provides the “brain” and the “body” of the feature.
Method 1: Return a Component Definition (The “Headless UI” Approach)
This is the most common method I use when I want to give the user a ready-made component.
The hook returns a function that acts as a React component, which you can then render in your JSX.
Let’s look at an example of a simple “Price Alert” banner used in a US-based e-commerce application.
import React, { useState } from 'react';
// This is our custom hook
const usePriceAlert = (initialPrice) => {
const [price, setPrice] = useState(initialPrice);
// We define a component inside the hook (or outside if it doesn't need closure)
const AlertBanner = ({ threshold }) => {
if (price > threshold) {
return (
<div style={{ padding: '10px', backgroundColor: '#ffe6e6', border: '1px solid red', borderRadius: '4px', margin: '10px 0' }}>
<strong>Alert:</strong> Current price of $ {price} exceeds your limit of $ {threshold}!
</div>
);
}
return null;
};
return { setPrice, AlertBanner };
};
// How we use it in a real application
const ShoppingCart = () => {
const { setPrice, AlertBanner } = usePriceAlert(150);
return (
<div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
<h1>Seattle Tech Store - Checkout</h1>
{/* We render the AlertBanner returned by the hook */}
<AlertBanner threshold={100} />
<p>Your current item price is tracked.</p>
<button onClick={() => setPrice(120)}>Update Price to $120</button>
<button onClick={() => setPrice(80)}>Update Price to $80</button>
</div>
);
};
export default ShoppingCart;I executed the above example code and added the screenshot below.

I prefer this method because it keeps the JSX separate from the logic while still allowing them to communicate.
It feels very natural to destructure AlertBanner and drop it right into the return statement of my page.
Method 2: Return JSX Elements Directly
Another way is to return the actual JSX elements as a variable from your hook. I often use this for “Loading” states or “Empty” views in dashboard applications.
In this example, we’ll simulate a Credit Score checker for a US banking app.
import React, { useState, useEffect } from 'react';
const useCreditScore = (userId) => {
const [score, setScore] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Simulating an API call to a US Credit Bureau
setTimeout(() => {
setScore(720);
setLoading(false);
}, 2000);
}, [userId]);
// We create a JSX element based on the state
const ScoreView = (
<div style={{ marginTop: '20px', padding: '15px', background: '#f0f4f8', borderRadius: '8px' }}>
{loading ? (
<p>Fetching your FICO score from the bureau...</p>
) : (
<h3>Your Current Credit Score: <span style={{ color: '#2ecc71' }}>{score}</span></h3>
)}
</div>
);
return { ScoreView, score };
};
const FinanceDashboard = () => {
const { ScoreView, score } = useCreditScore('user_99');
return (
<div style={{ fontFamily: 'Arial, sans-serif', textAlign: 'center', padding: '50px' }}>
<h2>Welcome back, Alex</h2>
<p>Financial Summary for February 2026</p>
{/* Here we just place the variable directly */}
{ScoreView}
{score && <p>You are in the "Excellent" range for US lending standards.</p>}
</div>
);
};
export default FinanceDashboard;I executed the above example code and added the screenshot below.

This method is quick and works well for static pieces of UI that don’t need their own props. Just be careful, if the UI is very complex, Method 1 is usually a cleaner architectural choice.
Method 3: Return a Factory Function for Dynamic Components
Sometimes you need to return a component that is highly dynamic and depends on external data.
In this case, your hook returns a function that takes arguments and returns a component.
I used this pattern recently for a Real Estate application in Texas to display different property tax disclosures.
import React from 'react';
const useTaxDisclosure = (stateCode) => {
// A factory function that returns a specific component based on state law
const getDisclosureComponent = (county) => {
return function Disclosure({ propertyValue }) {
const taxRate = stateCode === 'TX' ? 0.018 : 0.012; // Example rates
const estimatedTax = (propertyValue * taxRate).toLocaleString('en-US');
return (
<div style={{ fontSize: '0.9rem', color: '#555', fontStyle: 'italic', borderLeft: '4px solid #ccc', paddingLeft: '10px' }}>
Disclosure: In {county} County, {stateCode}, your estimated annual property tax
on a $ {propertyValue.toLocaleString()} home is approximately $ {estimatedTax}.
</div>
);
};
};
return { getDisclosureComponent };
};
const PropertyListing = () => {
const { getDisclosureComponent } = useTaxDisclosure('TX');
// We generate a specific component for Travis County
const AustinDisclosure = getDisclosureComponent('Travis');
return (
<div style={{ padding: '30px' }}>
<h1>Modern Home in Austin, TX</h1>
<p>Listed Price: $750,000</p>
{/* Rendering the dynamic component generated by the hook */}
<AustinDisclosure propertyValue={750000} />
<button style={{ marginTop: '20px' }}>Schedule a Tour</button>
</div>
);
};
export default PropertyListing;I executed the above example code and added the screenshot below.

This is the most powerful method, though it is slightly more advanced. It allows you to inject logic into the component definition itself before it even renders.
Best Practices I’ve Learned
Over the years, I have noticed that developers often overcomplicate this pattern.
One big mistake is re-creating the component definition on every single render.
If your component doesn’t need variables from the hook’s scope, define it outside the hook.
If it does need hook variables, make sure to use useMemo or useCallback to keep it stable.
This prevents unnecessary re-renders that can slow down your application.
Another tip is to keep your hook names descriptive, like useModal or useFeedbackUI.
This makes it clear to other developers that this hook is responsible for returning visual elements.
Conclusion
Returning a component from a React hook is a great way to build modular, clean code.
Whether you return a component definition, a JSX variable, or a factory function, the goal is reusability.
I hope these examples based on real-world US scenarios help you understand when to use each approach.
Try implementing these in your next project to see how much cleaner your main components become.
You may also like to read:
- How to Pass Values to Components in React
- 5 Best React Table Components in 2026
- Prop Distribution in React Functional Components
- React Google Calendar Component

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.