Meet use-async-view for React

Jorge Cortez
November 30, 2025
Read time
Tech Talk

Yes, it happened again, although I had a clear goal in mind for the week, I ended up leaving my current projects aside to make one more thing (plus got a ton of stuff I needed to work on). This time I made an NPM module, nothing super fancy but something I believe will be pretty useful in some cases. I present you use-async-view. A simple (says who made it), easy to implement hook for managing the state of asynchronous views in your React and React Native applications.

In a way my ‍

‍hook is pretty similar to React’s new Suspense component which “lets you display a fallback until its children have finished loading” with the difference that I allow you to display a fallback, a loading, and an error components with the addition of a reload function. Plus this was made for both React and React Native so there’s that. Anyway let’s jump to how the hook works!

TL;DR

use-async-view is a tiny hook for “fetch something, then show the right screen” flows.

  • You pass it:
    • an async loadFn
    • components for each state (Fallback, Loading, Success, Error)
  • It gives you:
    • a RenderedView element you can drop straight into your JSX
    • a reload function to re-run the request
    • an internal state machine (idle → loading → success | error) so you don’t have to re-write it in every component

Use it when you want simple async views without pulling in heavier data-fetching libraries.

Quickstart

As any other node module, you need to start by getting it installed, for that the command thankfully is really easy, just do the following:

npm i use-async-view
# orp
npm add use-async-view

Okay, I’ve installed it in my project, now what? well to be completely honest this would be literally the bare minimum.

import { useAsyncView } from 'use-async-view';

type Data = { name: string };

//Get your async function
const LoadData = async () => {
  return fetch('https://dummyjson.com/products?delay=2000')
  .then(res => res.json());
}

//The component you want to use to render the data
const cards = ({ data }) => { 
  return data.products.map((product) => (
    <div key={product.id} className="card">
      <h3>{product.title}</h3>
      <p>{product.description}</p>
    </div>
  ));
}

//use it!
export default function App() {
  const { RenderedView, reload } = useAsyncView({
    loadFn: LoadData,
    Success: cards, 
  });

  return (
    <div>
      <h1>Initial Load</h1>
      {RenderedView}
      <button onClick={reload}>Reload</button>
    </div>
  );
}

Pretty simple right? no state management needed, you pass an async function and the component you want to use to display the response, easy as that, by default the hook will manage an error, loading, and response states. and the setup above is the bare minimum you need to do, just the component and a function, now this will yield you the following:

And this is what it will show if there was an issue loading the information:

Now, as I mentioned above, this is the minimum setup for the hook to work! there are multiple things you can add to it, let me explain them all:

//Declaring the hook
const { RenderedView, reload } = useAsyncView({
  loadFn: () => fetch('https://dummyjson.com/products?delay=2000').then(res => res.json()), //Async function returning a payload
  Fallback: () => <button onClick={reload}>Load user</button>, // Component to render while in idle state
  Loading: () => <Spinner />, //Component to show while the function is "Loading"
  Success: ({ data }) => <UserCard user={data} />, //Component to render on success, receives loaded data
  auto: false, //Defines whether to automatically trigger the load on mount, true by default
});

‍With this, you don’t have to bother with handling the state of the information that is being loaded through your parent view or to be concerned with re-renders being caused by the state changing. everything is already handled for you!

What else is included?

When I made this Module I included a couple of additional components for people to use, just to make it easier to reuse between codebases, one of them is the LoadingView which was made to be a flexible loading component to manage reusability, here’s how to use it along with it’s different options:

import { LoadingView } from 'use-async-view';

// Basic usage
<LoadingView />

// With custom text
<LoadingView text="Loading your data..." />

// With custom loader
<LoadingView 
  text="Please wait..."
  loader={<MyCustomSpinner />}
/>

// Show loader explicitly
<LoadingView showLoader={true} text="Loading..." />

And finally  an ErrorView component, same case as for the LoadingView:

import { ErrorView } from 'use-async-view';

// Basic usage
<ErrorView message="Failed to load data" />

// With error details
<ErrorView 
  message="Failed to load data"
  error={error}
/>

// With retry function
<ErrorView 
  message="Failed to load data"
  error={error}
  runFunction={handleRetry}
/>

‍Why choose this over the usual approaches?

To be clear: I’m not saying you must use this hook. It’s just one more option in your toolbox. and I believe it could more than definitely help and I’d be really happy to know this can make other people’s projects easier to handle but what you use in your project is up to you. what I can say is that this hook could help you out for the following points:

  • There is no need for re-building the same state machine every time you need to load data.
  • Easily manage the “view per state” via components, keeping your project reusable and seamless.
  • One place to wire reload, errors, and display logic.

It’s not a replacement for full data-fetching libraries, it’s more for those “I just need to load something and show the right view” moments.

Anything to add?

  • Cancellation: Do you abort in-flight calls on unmount or overlapping reloads?
  • Dependencies: None! this project uses just React’s basic hooks to work, no additional dependencies
  • Concurrency: IF someone tries to spam the reload function there is a guard that will prevent it, also the hook stops any calls on unmount
  • SSR: This component is not thought for Server Side rendering
  • Status values: Exact strings returned by status (e.g., "idle" | "loading" | "success" | "error").

When not to use it

If you need caching, deduping, infinite scrolling, or optimistic updates, use react-query/SWR. use-async-view shines for simple “fetch then show a view” moments.

Read Next

Explore Blog
Catching up after a month off, here’s what I’ve been working on: new JS 101 posts, a big update to the Stardew Valley Progress Tracker, Webtricks milestones, and more.
Projects, Updates, and Why haven’t I posted recently
While updating my old Stardew Valley project, I ran into a giant JSON file, and ended up building a VS Code extension to generate TypeScript types. It's now live!
I accidentally made a VS Code extension
I spent a month testing Webflow’s Model Context Protocol (MCP) on 5 production sites. Here’s what worked for SEO, CMS management, and what still feels unfinished.
Testing Webflow MCP: SEO Wins, CMS Experiments, and Lessons After 1 Month