Conversation
Plan for extracting business logic from UI components into reusable hooks, utilities, and unstyled primitive components via a tree-shakeable subpath export at @tambo-ai/react/primitives. Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
The plan has internal inconsistencies that will block clean implementation: it alternates between primitives living in react-sdk/src/primitives and in a separate packages/primitives-ui, despite explicitly choosing “no new package”. It also presents conflicting package.json#exports examples (source TS vs built artifacts), leaving the shipping contract ambiguous. Finally, some hook sketches embed UI/icon elements, which undermines the stated goal of unstyled, tree-shakeable primitives.
Summary of changes
Added a new plan document
- Introduced
plans/2026-01-26-feat-react-sdk-primitives-plan.mddescribing a tree-shakeable subpath export:@tambo-ai/react/primitives. - Documented:
- Proposed architecture and module layout (
hooks/,utils/) for primitives. - Example consumer usage imports (e.g.
useTamboAutoScroll,useMessageInputState,formatToolResult). - Proposed interfaces for key hooks/utilities.
- A 10-step migration plan starting with in-place refactors in
ui-registry, then addingpackage.jsonexports, then extracting/moving hooks and utilities. - Testing strategy, acceptance criteria, backwards compatibility requirements, and out-of-scope items.
- Proposed architecture and module layout (
- Included two
package.jsonexport examples: a simplified TS-source export snippet and a build-artifacts export snippet for ESM/CJS/types.
alecf
left a comment
There was a problem hiding this comment.
Really glad to see this shared code actually shared!
My high-level notes:
- I personally feel like putting this in a separate library feels cleaner - I have found it hard/frustrating to have a separate "mcp" subpackage - it's very hard to maintain strict dependency boundaries especially with the AI tools - they're all to happy to import across library boundaries and just drag everything into the main package
- I personally like "core" more than "primitives" - its a pretty common "base' package name for a lot of projects (you often see it when you have a project that consists of multiple npm libraries, you might not even know that "core" was in there because you're pulling in some higher-level library
- I don't think you need two sub packages (primitives and primitives/utils) - it seems like "primitives" is mostly just types anyway
- It kind of feels like these functions are all kind of extract as-is from the components (I'm sure that's because that's exactly what they are!) but that makes them feel like a kind of random hodge-podge of random association of items, even within the library. Personally I hate directories called "hooks" because hooks are the impelmentation detail, and really they should be organized semantically, e.g.
input/use-message-input-state.ts
input/draft-persistence.ts
input/keyboard-shortcuts.ts
suggestions/use-suggestion-merge.ts
tools/use-tool-response.ts
tools/tool-status.ts
content/use-message-content.ts
content/content-transforms.ts
content/content-??.ts
- I think of "utils" as a kind of "misc stuff until we figure out where this goes" - so personally I'd avoid starting a library with utils.ts files - make every filename count
There was a problem hiding this comment.
The plan has several internal inconsistencies that will block clean implementation: it mixes TS-source vs build-artifact exports, and it contradicts “no new package” by targeting packages/primitives-ui in Steps 3–8. It also violates its own UI-agnostic/tree-shaking stance by returning JSX icons in a primitives hook example. Finally, SSR behavior around document/sessionStorage should be made explicit and enforced in the steps/examples, and the backwards-compat deprecation strategy needs a clearer, non-noisy policy.
Summary of changes
Added a new plan document for @tambo-ai/react/primitives
- Introduced
plans/2026-01-26-feat-react-sdk-primitives-plan.mddescribing a tree-shakeable subpath export:@tambo-ai/react/primitives. - Documented:
- Design goals: extracting business logic from
ui-registrycomponents into hooks/utilities/unstyled primitives. - A dedicated Tree-shaking Requirements section (e.g., avoid module side effects, consider
"sideEffects": false, caution with barrel exports). - Proposed module layout under
react-sdk/src/primitives/{hooks,utils}plus consumer usage examples. - Sketches of hook/util interfaces (
useTamboAutoScroll,useMessageInputState,formatToolResult, etc.). - A 10-step migration plan starting with in-place refactors, adding
package.json#exports, extracting utilities/hooks, and validating via tests. - Testing strategy, backwards compatibility requirements, and acceptance criteria.
- Design goals: extracting business logic from
|
@alecf lots here, inline responses:
Happy to make that switch. I initially had it as a separate library, but everything from the new package is so dependent on
Here's my thinking:
Originally, I think I had it called
Agreed. I much prefer fractal-style "feature" folders so I'll probably follow something similar to that here. |
|
@lachieh all sounds good - I think I personally feel like "primitives" works in radix because a "button" is a primitive UI element even outside of radix... but I wouldn't say that a content hook is primitive hook? or that a transformer function is a "primitive" function? I dunno.. not a hill I need to die on but it just feels a little off. I'd also be fine with changing our internal package to |
|
Mm yeah that's a good point. The other name I threw around was Edit: Ended up going with |
- Rename package from primitives to @tambo-ai/react-ui-base - Replace hooks/utils folders with feature folders: - message-input/, message/, thread/, resources/, scroll/ - Update subpath exports to match feature structure - Colocate hooks and utilities by domain Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: CharlieHelps <[email protected]>
TL;DR
@tambo-ai/react/primitivessubpath export containing hooks, utilities, and unstyled primitive components extracted from ui-registrySummary
message-input.tsx,message.tsx) into reusable primitives