Skip to content

docs(react,0.9): fix quick-start example#1250

Merged
andrewkolos merged 3 commits intomainfrom
docs/react-readme-quickstart-rewrite
Apr 22, 2026
Merged

docs(react,0.9): fix quick-start example#1250
andrewkolos merged 3 commits intomainfrom
docs/react-readme-quickstart-rewrite

Conversation

@andrewkolos
Copy link
Copy Markdown
Collaborator

Fixes #1223. Supersedes #1225, which attempted to fix the quick start by in part by making processCreateSurfaceMessage idempotent in web_core. #1238 has since clarified that duplicate createSurface should error. This PR doesn't modify web_core.

To summarize, this PR fixes the quick-start code for the react renderer so that it actually runs while also trying to make things easier to follow for new users.

  • I tried to make the quick start closer to what a complete (but basic) app would look like (while staying focused on being a minimal hello-world example). For example, the message-processing callback isn't picky about surface names (i.e. it doesn't only listen for a surface with ID="main-chat").
  • useState is now used for hooking up the listener to the message processor. This avoids the fatal double message processing errors (useEffect doesn't guarantee only being called once, and react strict mode intentionally runs these hooks twice).
  • Like the succeeded PR, this one adds some hard-coded messages so that the user actually sees something on their screen besides a user message. A simple hello-world-esque Text component is used. Some breadcrumbs are also added to the readme as natural springboards for readers to use to learn more about specific conceepts (i.e. message types and paths).

Testing/verification instructions

Create a fresh react project and follow the quick start from the readme. Be careful if you decide to use an agent, as they can sometimes silently modify code instead of directly copy/pasting from the readme.

Pre-launch Checklist

If you need help, consider asking for advice on the discussion board.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the React renderer's README with a more comprehensive 'Quick Start' guide, including core concept definitions and an improved code example that handles multiple surfaces and various message types. A review comment suggests enhancing the example's useEffect hook to handle surface deletion events, ensuring the UI stays synchronized with the agent's state.

Comment thread renderers/react/README.md
Comment on lines 58 to 63
useEffect(() => {
// 2. Listen for surface creation
const sub = processor.onSurfaceCreated(s => {
// You can filter by surfaceId or manage multiple surfaces
if (s.id === 'main-chat') setSurface(s);
});

// 3. Process messages from your agent (e.g., via WebSocket or SSE)
// For demo purposes, we manually process a creation message:
processor.processMessages([{
version: 'v0.9',
createSurface: {
surfaceId: 'main-chat',
catalogId: basicCatalog.id // 'https://a2ui.org/specification/v0_9/basic_catalog.json'
}
}]);

return () => sub.unsubscribe();
const surfaceCreatedSub = processor.onSurfaceCreated(() =>
setSurfaces(Array.from(processor.model.surfacesMap.values()))
);
return () => surfaceCreatedSub.unsubscribe();
}, [processor]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current useEffect only handles surface creation. To keep the UI in sync with the agent's state, it should also handle surface deletion events. This ensures that surfaces removed by the agent (via deleteSurface messages) are correctly removed from the React state and the UI.

Suggested change
useEffect(() => {
// 2. Listen for surface creation
const sub = processor.onSurfaceCreated(s => {
// You can filter by surfaceId or manage multiple surfaces
if (s.id === 'main-chat') setSurface(s);
});
// 3. Process messages from your agent (e.g., via WebSocket or SSE)
// For demo purposes, we manually process a creation message:
processor.processMessages([{
version: 'v0.9',
createSurface: {
surfaceId: 'main-chat',
catalogId: basicCatalog.id // 'https://a2ui.org/specification/v0_9/basic_catalog.json'
}
}]);
return () => sub.unsubscribe();
const surfaceCreatedSub = processor.onSurfaceCreated(() =>
setSurfaces(Array.from(processor.model.surfacesMap.values()))
);
return () => surfaceCreatedSub.unsubscribe();
}, [processor]);
useEffect(() => {
const sync = () => setSurfaces(Array.from(processor.model.surfacesMap.values()));
const subCreated = processor.onSurfaceCreated(sync);
const subDeleted = processor.onSurfaceDeleted(sync);
return () => {
subCreated.unsubscribe();
subDeleted.unsubscribe();
};
}, [processor]);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did consider this as I do want the quick start code to complete enough to convert into a functional app. I decided against as I wanted to balance that idea with keeping the code as simple as possible. However, I think that given that this is a "quick start" and not just a "hello world" or step 1 of a code lab, I think adding a listener for surface deletion makes sense.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrewkolos andrewkolos marked this pull request as ready for review April 21, 2026 20:29
@andrewkolos andrewkolos changed the title docs(react): fix quick-start example docs(react,0.9): fix quick-start example Apr 21, 2026
@andrewkolos
Copy link
Copy Markdown
Collaborator Author

@ditman @jacobsimionato This supersedes #1225. PTAL to keep me honest and make sure I've sufficiently addressed your feedback from that PR.

Copy link
Copy Markdown
Collaborator

@jacobsimionato jacobsimionato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great - thank you for giving our users a smooth onboarding experience! I'm a React newbie, but based on what you were telling me about useState and useEffect, this seems correct and more elegant.

@andrewkolos
Copy link
Copy Markdown
Collaborator Author

andrewkolos commented Apr 22, 2026

I've come across useSyncExternalStore1, which might be better to use in place of setState. Looks like the former was introduced along with an (opt-in) change in renderer behavior in React 18, which makes sense given my last significant experience working with React was with 17. AFAICT the current approach in this PR is fine for a quick start and is worth landing now as it is much better than the current state of "doesn't compile."

Footnotes

  1. https://react.dev/reference/react/useSyncExternalStore

@andrewkolos andrewkolos merged commit 498b48f into main Apr 22, 2026
12 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in A2UI Apr 22, 2026
@andrewkolos andrewkolos deleted the docs/react-readme-quickstart-rewrite branch April 22, 2026 06:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

2 participants