Skip to content

perf: React Compiler compat, bundle size & architecture improvements#1465

Open
haddoumounir wants to merge 8 commits intovercel:mainfrom
haddoumounir:fix/react-optimizations
Open

perf: React Compiler compat, bundle size & architecture improvements#1465
haddoumounir wants to merge 8 commits intovercel:mainfrom
haddoumounir:fix/react-optimizations

Conversation

@haddoumounir
Copy link
Copy Markdown

Summary

  • Bundle size: Migrate all motionm components + wrap app in LazyMotion provider (~30kb savings), replace dangerouslySetInnerHTML script with next/script
  • Image optimization: Replace native <img> with next/image in image-editor, console, and model-selector
  • React Compiler compatibility: Fix refs-during-render patterns, replace effect-based state sync with derived state and conditional setState during render
  • State consolidation: Merge related useState calls into useReducer in ActiveChatProvider and PureArtifact
  • Architecture: Extract large components into focused sub-components and custom hooks:
    • PureArtifact (412 → 179 lines): ArtifactHeader, ArtifactContentArea, useArtifactDocuments
    • PureMultimodalInput (475 → 292 lines): useFileUpload, useSlashCommands
    • PromptInput (429 → 137 lines): usePromptInputFiles, useDropHandlers
  • Accessibility: Add prefers-reduced-motion CSS media query, use-reduced-motion hook, remove autoFocus
  • SEO: Add page metadata on chat routes

Switch from `motion` to `m` components across all files and wrap the
app in a LazyMotion provider with `domAnimation` features. This avoids
bundling the full framer-motion runtime (~30kb savings).

Also replaces dangerouslySetInnerHTML script with next/script for
safer loading.
Use Next.js Image component for automatic optimization, lazy loading,
and responsive sizing in image-editor, console, and model-selector.
- Derive hasReasoning inline instead of useState+useEffect latch
- Use conditional setState during render instead of ref patterns for
  detecting prop/state changes (shell, sheet-editor, use-messages)
- Add portalTarget state in text-editor to avoid reading ref during render

These patterns are compatible with React Compiler optimization.
Merge currentModelId, input, showCreditCardAlert, and
hasLoadedCookieModel into a single useReducer in ActiveChatProvider.
Make setInput and setShowCreditCardAlert stable callbacks with empty
dependency arrays since no callers use functional updates.
Break PureArtifact (412 lines) into focused pieces:
- ArtifactHeader: title, save status, version badge
- ArtifactContentArea: content renderer, toolbar, version footer
- useArtifactDocuments: document fetching, versioning, content saving

Also consolidates mode/document/versionIndex/isContentDirty into a
single useReducer and fixes try/finally patterns for compiler compat.
Extract useFileUpload and useSlashCommands hooks from
PureMultimodalInput (475 → 292 lines). Simplify toolbar by removing
redundant isHovered state and driving tooltip from selection state.
…Input

Break PromptInput (429 → 137 lines) by extracting file management
into usePromptInputFiles hook and drag-and-drop setup into
useDropHandlers hook. Add onKeyDown support to input-group.
- Add prefers-reduced-motion CSS media query to disable animations
- Add page metadata for SEO on chat routes
- Add use-reduced-motion hook for runtime motion preference detection
- Remove autoFocus from auth form input (a11y best practice)
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 22, 2026

@haddoumounir is attempting to deploy a commit to the Templates Test vtest314 Team on Vercel.

A member of the Team first needs to authorize it.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ai-chatbot Ready Ready Preview, Comment, Open in v0 Mar 27, 2026 10:08am
chatbot Ready Ready Preview, Comment, Open in v0 Mar 27, 2026 10:08am

@kwasham
Copy link
Copy Markdown

kwasham commented Mar 31, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 946dc03abe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +60 to +62
setPrevChatId(chatId);
stop();
setArtifact(initialArtifactData);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Move chat-switch side effects out of render

This block runs during render and invokes stop() plus setArtifact(...), both of which update state outside ChatShell. On chat navigation, React can run/discard renders (especially in Strict/Concurrent mode), so executing these mutations in render can trigger Cannot update a component while rendering a different component warnings and non-deterministic resets/cancellations. Keep only local derivation in render and move these side effects into a useEffect keyed by chatId.

Useful? React with 👍 / 👎.

Comment on lines 288 to 290
if (artifact.status === "streaming" || !isContentDirty) {
setArtifact((currentArtifact) => ({
...currentArtifact,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid mutating artifact store during render

When documents changes, this render-path branch calls setArtifact(...) immediately. Because setArtifact mutates shared SWR-backed state, this is a cross-component update during render, which can warn in React and run multiple times on interrupted/double renders, causing unstable artifact content synchronization. The document→artifact sync should happen in an effect tied to documents instead of directly in render.

Useful? React with 👍 / 👎.

Comment on lines +377 to +379
if (!hasInitialized && localStorageInput) {
setHasInitialized(true);
setInput(localStorageInput);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Initialize input without updating parent during render

This initialization path calls setInput(localStorageInput) while PureMultimodalInput is rendering. Since setInput updates ActiveChatProvider state, this is a parent update during child render and can produce React warnings plus duplicate initialization under Strict Mode. Initialize the draft value in an effect (or in the provider’s initial state) so the update happens after render commit.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants