Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Streamable UIs Disappear in Production Builds When Calling .done() #1558

Open
amcclure-super opened this issue May 10, 2024 · 12 comments
Assignees
Labels

Comments

@amcclure-super
Copy link

amcclure-super commented May 10, 2024

Description

When nesting createStreamableUI values inside another createStreamableUI, the final result disappears in production builds. The issue does not surface in local development.

Here is a reproducible example which works in dev but not after running pnpm build: https://github.com/amcclure-super/ai-streamableUI

Here are a couple videos. The first video in a dev build shows that the values do not disappear after marking ui.done(). The second video shows the values disappearing after ui.done() is called in a production build.

In dev, the UI does not disappear after ui.done():

2024-05-10.16-27-27.mp4

In a production build, the UI disappears after ui.done() is called:

2024-05-10.16-28-35.mp4

Code example

Full repo: https://github.com/amcclure-super/ai-streamableUI

Relevant code:

// page.tsx
"use client";

import { useState } from "react";
import { ClientMessage } from "./actions";
import { useActions, useUIState } from "ai/rsc";
import { nanoid } from "nanoid";

export default function Home() {
  const [input, setInput] = useState<string>("");
  const [conversation, setConversation] = useUIState();
  const { submitUserMessage } = useActions();

  return (
    <div>
      <div>
        {conversation.map((message: ClientMessage) => (
          <div key={message.id}>
            {message.role}: {message.display}
          </div>
        ))}
      </div>

      <div>
        <input
          type="text"
          value={input}
          onChange={(event) => {
            setInput(event.target.value);
          }}
        />
        <button
          onClick={async () => {
            setConversation((currentConversation: ClientMessage[]) => [
              ...currentConversation,
              { id: nanoid(), role: "user", display: input }
            ]);

            const message = await submitUserMessage(input);

            setConversation((currentConversation: ClientMessage[]) => [
              ...currentConversation,
              message
            ]);
          }}
        >
          Send Message
        </button>
      </div>
    </div>
  );
}
// actions.tsx
import "server-only";

import { createAI, createStreamableUI } from "ai/rsc";
import { ReactNode } from "react";

export interface ServerMessage {
  role: "user" | "assistant";
  content: string;
}

export interface ClientMessage {
  id: string;
  role: "user" | "assistant";
  display: ReactNode;
}

export async function submitUserMessage(userInput: string) {
  "use server";

  let ui, uiStreams;
  try {
    ui = createStreamableUI(<div>Loading...</div>);

    (async () => {
      try {
        ui.update(<div>UI Update 1</div>);
        uiStreams = Array.from({ length: 3 }).map((_, i) => {
          return createStreamableUI(<div>Nested {i}</div>);
        });

        await new Promise((resolve) => setTimeout(() => resolve(true), 3000)); // wait 3 seconds

        ui.update(
          <div>
            Composed UI{" "}
            {uiStreams.map((u) => {
              return u.value;
            })}
          </div>
        );
        await new Promise((resolve) => setTimeout(() => resolve(true), 3000)); // wait 3 seconds
      } catch (e) {
      } finally {
        for (let i = 0; i < uiStreams?.length ?? 0; i++) {
          uiStreams[i].done();
        }
        ui.done();
      }
    })();
  } catch (e) {}

  return {
    id: Date.now(),
    display: <div>{ui.value}</div>
  };
}
@armando-immunefi
Copy link

confirming this on nextjs 14.2.3

@shuding shuding self-assigned this May 14, 2024
@amithmkini
Copy link

Same here. I should also add that nested streamable UIs were working in Next.js 14.1.3

@lionelrudaz
Copy link

Same issue with AI 3.1.9 and Next 14.2.2.

My product is not yet on production, so that's OK, but that's bummer if you rely on this feature 😅

I hope there will be a fix soon. Let us know if we can provide more details.

@allenchuang
Copy link

Same thing here... locally it works fine not sure what the issue is. If someone is willing to drop a hint I'd be welcome to help take a look

@unstubbable
Copy link
Contributor

unstubbable commented May 21, 2024

This issue has been fixed in React, and will be provided with the upcoming Next.js release. You can verify that in your repro with these commands:

npm add ai zod next@canary react@beta react-dom@beta --legacy-peer-deps
npm run build && npm start

Reference:

Related:

@allenchuang
Copy link

Thank you so much! You're a hero 🫡

@lionelrudaz
Copy link

Thanks @unstubbable, that's awesome. Can you point to the Next.js release that we'll have to upgrade to? Will it be 14.3.0 or a minor release of 14.2?

@unstubbable
Copy link
Contributor

Just looking in from the outside, it appears that the team is currently in the process of preparing the release of Next.js 15, which will include React 19. The aforementioned React fixes have been integrated here: vercel/next.js#65058

@allenchuang
Copy link

hey @unstubbable i actually tested by upgrading to your suggested versions

npm add ai zod next@canary react@beta react-dom@beta --legacy-peer-deps
npm run build && npm start

however, this not only doesn't solve it, the issue now appears locally.. 🤔 any ideas here?

@unstubbable
Copy link
Contributor

Did you test it with https://github.com/amcclure-super/ai-streamableUI, or somewhere else?

@allenchuang
Copy link

allenchuang commented May 23, 2024

I tested in my own app based on https://github.com/vercel/ai-chatbot where i'm using function_calling to call external APIs to get back a response. Does it have to be the repo above?

@unstubbable
Copy link
Contributor

I tested in my own app based on https://github.com/vercel/ai-chatbot where i'm using function_calling to call external APIs to get back a response. Does it have to be the repo above?

No, but if the update doesn't help your case it's possible that you have a different situation. In this case it might make sense to create a separate issue with your own minimal reproduction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants