# refactor\(web\): resting image previews and queued attachments are their own components · PR \#22719 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/22719)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: refactor/chat-composer-resting-previews

Target branch: refactor/chat-composer-attachments

Head commit: 7ccabf41dd3ddd91da2f990c070845d79e4a9e6f

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/22719)

[Commits](https://git.cafe/pingdotgg/t3code/pull/22719/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/22719/diffs?format=markdown)

## Description

---

Continues the ChatComposer split.

The thumbnails shown next to the prompt while the composer rests are now `ComposerRestingImagePreviews`, which ChatComposer renders in two places. The attachments of a queued message being edited are now `ComposerQueuedAttachmentRow`. Both live in `ComposerAttachmentStrip.tsx`. The resting previews compute their own preview counts. ChatComposer builds them once as an element const and passes its "+N more" handler directly. This adds no `useStableCallback` uses; ChatComposer keeps the 34 it has on main. Behavior is unchanged.

| | before | this PR |
|---|---|---|
| ChatComposer.tsx lines | 7,016 | 6,923 |
| ComposerAttachmentStrip.tsx lines | 535 | 656 |
| Renders, 20 typed chars | 678 | 678 |

Renders measured with the shared fiber counter (`rc-init.js`) on a seeded dev server: typing 20 characters into an existing thread's composer, median of 3 runs. This layer is for readability; it does not change renders.

Tests run: `vp lint` and `vp fmt --check` on the changed files, `tsc-rs --noEmit` for apps/web, and `reactCompilerCoverage.test.ts` for the changed files (0 bailouts).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

