vercel-labs / vercel-labs/react-native-diffs

DiffsView renders at 0 height in content-sized parents (workaround for mrousavy/nitro#1199)

Open
#1 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
16
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Summary

DiffsView requires its parent to supply explicit dimensions (typically flex: 1 or a fixed height). When embedded in a content-sized parent — e.g. a chat bubble or a <View> with no flex — it renders at 0 height and content is invisible.

Reproduce

<View style={{ padding: 16 }}>
  <DiffsView content="```ts\nconst x = 1;\n```" colorScheme="light" />
</View>

Expected: bubble grows to fit the rendered code block.
Actual: bubble has 0 height.

Root cause

Nitro 0.35 does not propagate the underlying UIView's intrinsic size to Yoga. Confirmed by reading the nitrogen output:

  • nitrogen/generated/shared/c++/views/HybridDiffsComponent.hpp declares the shadow node as a plain ConcreteViewShadowNode<...>, without the MeasurableYogaNode / LeafYogaNode traits.
  • Yoga's LayoutableShadowNode::measureContent default returns zero, and is only consulted when MeasurableYogaNode is set on the shadow node traits (react-native YogaLayoutableShadowNode.cpp).

The framework-level fix is tracked at mrousavy/nitro#1199 — Support measureContent(...) for Nitro Views, opened by Marc Rousavy himself. As of writing it has 0 comments and no implementation.

Until Nitro adds measureContent, every Nitro view has the same limitation. The pragmatic mitigation at the consumer-library level is to surface the measured height through a callback so the JS parent can apply it as inline style.height.

Use case

Embedding DiffsView in a chat-style transcript: one Claude assistant turn per "bubble", each bubble's height driven by the rendered markdown. flex: 1 is not applicable because bubbles must be content-sized so they stack and the parent ScrollView can scroll across multiple turns.

Proposed solution

Add an optional callback prop to DiffsView:

export interface ContentSize {
  width: number;
  height: number;
}

export interface DiffsProps extends HybridViewProps {
  // ... existing props
  onContentSizeChange?: (size: ContentSize) => void;
}

Implementation outline (iOS):

  • Outer view becomes a small UIView subclass that fires a callback on layoutSubviews bounds change.
  • After setMarkdown (switched to setMarkdownManually for synchronous Core Text layout), call markdownTextView.boundingSize(for: width) and invoke the callback.
  • Coalesce same-size reports to avoid feedback loops when JS reflows on each report.

Caller pattern (sidecode wrapper, simplified):

function MarkdownView(props) {
  const [height, setHeight] = useState<number>();
  return (
    <DiffsView
      {...props}
      onContentSizeChange={{ f: (size) => setHeight(size.height) }}
      style={{ height }}
    />
  );
}

Backward compatible: existing flex: 1 consumers (e.g. v0.app/ios) are not affected; the new prop is opt-in.

Next step

Happy to submit a PR with the spec change, iOS implementation, and an updated example/ showcasing the chat-bubble pattern. Reference the work I've already done in a private fork — full diff is small (~50 LOC excluding nitrogen-generated output). Would you accept this approach?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read nitrogen/generated/shared/c++/views/HybridDiffsComponent.hpp and the existing DiffsView spec and iOS implementation. Review the Nitro #1199 limitation, then inspect the example/ entry point for the chat-bubble use case. Done means the optional content-size callback reports coalesced dimensions, existing flex consumers remain unaffected, and the example demonstrates content-sized rendering.

Written by the indexing model from the issue text.

Assessment

Tech stack
ios, react-native, typescript
Domain
frontend, mobile-dev
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.