Giselle
Willi Icon

Multi‑Model Composition

Auto-select the best model

Visual Agent Builder

Create agents in minutes

Knowledge Store

Access external data sources

GitHub Icon

GitHub AI Operations

Automates issues, PRs, and deployments with AI

Use Cases

Deep Researcher

AI-powered research and analysis

PRD Generator

Generate product requirements docs

GitHub Icon

Code Reviewer

Automated code review and feedback

Marketing Teams

Doc Updater

Keep documentation up to date

Users

Engineering Teams

AI-Native Startups

Automate workflows, ship faster

Solopreneurs & Fast Builders

Build and launch AI products, solo

Product-Led Engineers

Build, iterate, and ship faster with AI-powered development tools

Tech Writers & DevRel

Self-updating docs, more strategy time

Innovation Teams at Modern Enterprises

Embed AI workflows, scale innovation

Docs
Pricing
Blog
—
Sign UpArrow Icon
Giselle

Product

  • Multi-Model Composition
  • Visual Agent Builder
  • Knowledge Store
  • GitHub AI Operations

Solutions

  • Deep Researcher
  • PRD Generator
  • Code Reviewer
  • Doc Updater
  • AI-Native Startups
  • Solopreneurs & Fast Builders
  • Product-Led Engineers
  • Tech Writers & DevRel
  • Innovation Teams

Resources

  • Blogs
  • Open Source
  • Dictionary

Legal

  • Term
  • Privacy & Cookies

About

  • About Us
  • Contact Us

Build visually, deploy instantly.

© 2026 Giselle
GitHubLinkedInFacebookBlueskyXInstagramYouTube
Giselle

Build visually,
deploy instantly.

Product

  • Multi-Model Composition
  • Visual Agent Builder
  • Knowledge Store
  • GitHub AI Operations

Solutions

  • Deep Researcher
  • PRD Generator
  • Code Reviewer
  • Doc Updater
  • AI-Native Startups
  • Solopreneurs & Fast Builders
  • Product-Led Engineers
  • Tech Writers & DevRel
  • Innovation Teams

Resources

  • Blogs
  • Open Source
  • Dictionary

Legal

  • Term
  • Privacy & Cookies

About

  • About Us
  • Contact Us
© 2026 Giselle
GitHubLinkedInFacebookBlueskyXInstagramYouTube

We want to be clear about how we collect and use cookies so that you can have control over your browsing data.

If you continue to use Giselle, we will assume you are comfortable with our cookie usage.

Tech

Optimistic Transition: Why Next.js `loading.tsx` Isn’t Showing (App Router + middleware/proxy delay)

PUBLISHEDDECEMBER 24, 2025

Satoshi Toyama,
Founding Engineer
Optimistic Transition: Why Next.js loading.tsx isn’t showing with middleware/proxy delay (App Router)

Table of contents

  • 1) Why “just add a spinner” isn’t good enough
  • 2) Next.js is great at smooth transitions—until it isn’t
  • 3) The surprise: loading.tsx and streaming don’t start under middleware / proxy.ts delay
  • 4) Try it yourself: feel the No UI gap with proxy delay
  • 5) The fix: “Optimistic Transition” (destination-shaped overlay that bridges the No UI gap)
  • 6) How Giselle applies it: layout-persistent overlay + store-driven control
  • 7) When to use this pattern (and when not to)
  • 8) Takeaway

You open Giselle Playground, pick an app, type a request, and Run.

The best version of this experience is immediate:

  • you get an instant “we’re moving” acknowledgment
  • you see destination-shaped UI (task context + status)
  • you never wonder if your click registered

This instant feeling feels natural to users. However, in Next.js, it's surprisingly hard to achieve using only the framework's standard features.

In Next.js, when you implement a flow like an input screen → a results screen, it’s common to split it into separate pages. And it’s also common to use loading.tsx (or loading.js) to improve the experience until the next page renders. However, loading.tsx can only show after the next page’s response has started coming back—meaning while your proxy (formerly middleware) is still running, loading.tsx won’t show at all. That moment of dead air is what I’ll call the No UI gap.

In this post, I’ll use a sample application to show two things:

  • demonstrate how the No UI gap appears while proxy.ts is running
  • the UX pattern we use in Giselle to keep the experience snappy anyway: an Optimistic Transition (a destination-shaped overlay)

1) Why “just add a spinner” isn’t good enough

A spinner is fine for “this button is busy.”

But Playground is a destination-shaped flow:

  • after “Run”, users expect to land on a task surface
  • they want to see what’s happening in context (agent, input, status)
  • they want reassurance that the app has transitioned state

A spinner on the source screen says: “wait here.” A destination-like shell says: “you’re already on your way there.”

That difference matters most when navigation can’t even start rendering.

2) Next.js is great at smooth transitions—until it isn’t

In the App Router, two big tools improve perceived performance after the destination starts rendering:

loading.tsx: route-level render-time loading UI

A loading.tsx file can show while a segment is being prepared.

But the critical condition is timing:

  • loading.tsx can only render once Next.js has actually entered the destination segment and started rendering it.

Streaming (Suspense): progressive rendering once the response starts

Streaming helps once the server starts producing bytes for the destination.

Again, same boundary:

  • streaming only begins after rendering begins.

One boundary to remember

Both tools help after render starts.

If the delay happens before Next.js can start rendering the destination, these tools can’t show yet.

That’s exactly what middleware / proxy delay can create.

3) The surprise: loading.tsx and streaming don’t start under middleware / proxy.ts delay

To make this concrete, we’ll use a small sample app you can try in the browser: Next.js Loading Lab.

If you add a deterministic delay in request-time code (middleware/proxy), the timeline looks like this:

  1. user triggers navigation (Link, router.push, action → redirect, etc.)
  2. the request is held upstream by middleware / proxy delay
  3. only when that finishes can Next.js route + render the destination
  4. only then can loading.tsx / streaming appear

So when you feel the No UI gap, it’s not that Next “forgot” your loading.tsx. It’s that render-time UI can’t start yet because the request hasn’t reached rendering.

This is an ordering problem (request-time vs render-time), not a bug.

4) Try it yourself: feel the No UI gap with proxy delay

If this still feels abstract, let’s try it. The goal is to make the boundary undeniable by introducing a delay you can feel consistently.

Steps

  1. Open Next.js Loading Lab.
  2. In the left menu, select loading.tsx.
  3. On the right, enter a keyword and click Open Summary.
    • You should see the skeleton UI immediately, then the result page renders and fetches the Wikipedia summary.

Here is the baseline behavior without proxy delay: the loading.tsx skeleton shows immediately after click.

  1. Now go to Proxy (middleware) in the left menu and check Enable proxy.
  2. Set Proxy delay to something noticeable (e.g. 500–1500ms) and click Save.
  3. Repeat steps 2–3.
    • This time, you’ll notice a “dead air” period: the skeleton won’t appear until after the proxy delay has elapsed.

With proxy delay enabled, notice the No UI gap: nothing can render (including loading.tsx) until the proxy finishes.

What to observe

Under proxy delay, look for:

  • Before start: dead air (no route loading UI yet)
  • After start: the usual App Router loading/streaming behaviors finally become available

That split is the entire point of this post.

5) The fix: “Optimistic Transition” (destination-shaped overlay that bridges the No UI gap)

You can see this solution working in the same sample app. In Next.js Loading Lab, enable Proxy (middleware) with a noticeable delay, then open Optimistic Transition and click Open Summary. Unlike loading.tsx, the overlay shows immediately on click—bridging the dead air while the proxy is still running.

With proxy delay enabled, this is what it looks like: the overlay appears immediately on click, even while the proxy is still running.

When the No UI gap happens, the problem isn’t “we need a loader.”

The real problem is:

the destination hasn’t started yet—so the user can’t see evidence of progress or state transition.

The solution is to render immediate, destination-shaped UI on the client before the destination can render.

Definition

An Optimistic Transition is UI that appears immediately and acts as a proxy of the final destination experience—so the user feels the transition has started even before the destination can render.

  • not a generic spinner
  • not “please wait”
  • a faithful stand-in for the destination’s structure, showing what’s already known and honestly placeholdering what’s unknown

Why it works (relative to the boundary)

  • loading.tsx / streaming improve what happens after render begins.
  • Optimistic Transition improves what happens before render can begin.

This is exactly the gap middleware/proxy delay exposes.

6) How Giselle applies it: layout-persistent overlay + store-driven control

To cover the No UI gap, two things must be true:

  • the overlay must stay mounted during navigation
  • the current screen must be able to show it instantly (synchronously with the user action)

That leads to a clean division of responsibilities:

Want the code? Start with the demo + the real product

If you’re an engineer, you probably want to verify the claim by reading actual code. Two good starting points:

  • Next.js Loading Lab (repro demo): the exact lab used in this post is open-source at toyamarinyon/nextjs-loading-lab.
    • Proxy delay (request-time): proxy.ts
    • Proxy config cookies: app/api/config/route.ts, lib/config.ts
    • Overlay pattern: components/overlay.tsx (and the shared views under components/)
  • Giselle (production OSS): Giselle itself is open-source at giselles-ai/giselle.
    • Giselle is a monorepo: the Studio web app lives under apps/studio.giselles.ai (Next.js App Router).
    • Playground routes/UI start here: apps/studio.giselles.ai/app/(main)/playground
    • Tip: search within apps/studio.giselles.ai for keywords like overlay, transition, or optimistic to find the real implementation used in the product.

Placement: mount it in a persistent layout, not in page.tsx

If the overlay lives inside a page, it can unmount during navigation—exactly when you need it.

Mount it in a layout that persists across the transition (segment layout or root layout), and render it alongside {children}.

Control: trigger from the source screen (client), render from the layout

The source action (like “Run”) can:

  • synchronously show overlay with known context (agent name, input parameters, etc.)
  • start the async work / navigation

The overlay can then:

  • show truthful status (“Starting…”, “Waiting for response…”, etc.)
  • disappear when navigation completes (or when the destination tells it to hide)

Dismissal: make it explicit

Because a layout-mounted overlay persists, it won’t magically disappear. Make hiding explicit on success/failure:

  • success path: hide on arrival (destination mounts)
  • failure path: hide and show error + recovery affordance

7) When to use this pattern (and when not to)

Use Optimistic Transition when:

  • the user action is high-intent (Run / Generate / Publish / Checkout)
  • “dead air” would break trust
  • you can render a truthful destination-like shell immediately
  • you can define clear dismiss conditions

Avoid it when:

  • navigation is low-intent / link-browsing heavy
  • the action is not safely repeatable (unless you have idempotency)
  • you can’t define crisp success/failure/cancel handling

Pitfalls to design for:

  • Double-submit: users click again when they see nothing—disable triggers, consider idempotency keys
  • Failure recovery: show retry/back/cancel clearly
  • Honesty: no fake progress; show what you actually know (input, target, status)

8) Takeaway

If you remember one thing, make it this:

Next.js can only show loading.tsx / streaming after the destination has started rendering.

Middleware / proxy delay can block before that point—creating a No UI gap that render-time tools cannot cover.

Decision rule:

  • if your slow step is before rendering begins → use an Optimistic Transition (overlay)
  • if your slow step is after rendering begins → use loading.tsx and streaming

In Giselle Playground, we optimize for “instant” by making the transition feel continuous—even when navigation hasn’t started rendering yet.

Last edited onDECEMBER 24, 2025
  1. Top
  2. Arrow Right
  3. Blog
  4. Arrow Right
  5. Tech
  6. Arrow Right
  7. Optimistic Transition: Why Next.js `loading.tsx` Isn’t Showing (App Router + middleware/proxy delay)
Prev Arrow
Prev
Upgrading Stripe API version with AI-powered Speculative Implementation
Next Arrow
Next
The Thinnest Script Infrastructure — Made for Coding Agents

Try Giselle Free or Get a Demo

Supercharge your LLM insight journey -- from concept to development launch
Get Started - It's Free

Related Insights

Version control at the speed of thought
Tech

Version control at the speed of thought

Satoshi Toyama,
Founding Engineer
Git Worktree, My Way
Tech

Git Worktree, My Way

Satoshi Toyama,
Founding Engineer
The Thinnest Script Infrastructure — Made for Coding Agents
Tech

The Thinnest Script Infrastructure — Made for Coding Agents

Satoshi Toyama,
Founding Engineer