Jacob Paris
← Back to all content

Wait for a BullMQ job to complete with Remix Deferred Loaders

Remix's Defer API is a feature that allows you to return an unresolved Promise from a loader. The page will server-side render without waiting for the promise to resolve, and then when it finally does, the client will re-render with the new data.

This works especially well with queued jobs, where a user may try to look at result data before the job has finished processing. In this case, we can return a Promise that will resolve when the job is finished, and the client will re-render with the new data.

BullMQ is a modern, fast, and robust queue system for Node.js. It is a successor to the popular Bull library, and is built on top of Redis. If you've integrated BullMQ into your app, you can use the Defer API to return a Promise that will resolve when the job is finished.

import { defer, redirect } from "@remix-run/node"
import { processItemQueue } from "~/queue.server"
export async function loader({ params }: LoaderArgs) {
const hash = params.hash
if (!hash) {
return redirect("/")
const job = await processItemQueue.getJob(hash)
if (!job) {
return redirect("/")
const TIMEOUT_MILLISECONDS = 30 * 1000
return defer({
job: job.waitUntilFinished(
export default function Index() {
const data = useLoaderData()
return (
<Suspense fallback={<p> loading… </p>}>
errorElement={<p>Error loading job</p>}
{(results) => <pre>{results}</pre>}

As a next step, you can use event streams to update the page as the job progresses.

Professional headshot

Hi, I'm Jacob

Hey there! I'm a developer, designer, and digital nomad with a background in lean manufacturing.

About once per month, I send an email with new guides, new blog posts, and sneak peeks of what's coming next.

Everyone who subscribes gets access to the source code for this website and every example project for all my tutorials.

Stay up to date with everything I'm working on by entering your email below.

Unsubscribe at any time.