Skip to content

feat(input): cross-platform contentInset prop (iOS textContainerInset + Android scroll container)#442

Open
andreavrr wants to merge 4 commits into
software-mansion:mainfrom
andreavrr:feat/content-inset
Open

feat(input): cross-platform contentInset prop (iOS textContainerInset + Android scroll container)#442
andreavrr wants to merge 4 commits into
software-mansion:mainfrom
andreavrr:feat/content-inset

Conversation

@andreavrr

@andreavrr andreavrr commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Closes #426

Summary

Adds a cross-platform contentInset prop to EnrichedMarkdownTextInput: a content cushion that is part of the scrollable content and scrolls away with the text, instead of clipping the way style padding does today. top pushes the first line down (e.g. below a translucent header); bottom keeps room past the last line. It exposes the iOS UITextView.textContainerInset behaviour and brings the same to Android.

Problem

There is no prop for a content cushion that scrolls away with the text. The only workaround is style padding, and on both iOS and Android paddingVertical on the editor is clipped while the field scrolls (the cushion eats the text), and a long autofocused value does not reveal the end. iOS has UITextView.textContainerInset for exactly this, but it is not exposed; Android has no equivalent.

<EnrichedMarkdownTextInput
  autoFocus
  defaultValue={LONG_TEXT}
  scrollEnabled
  style={{ flex: 1, fontSize: 18, paddingVertical: 48 }}
/>

Minimal reproducible example (Expo SDK 56, RN 0.85.3, New Architecture):
https://github.com/andreavrr/enriched-markdown-content-inset-mre

API

contentInset?: { top: number; bottom: number; left: number; right: number };

Fix

  • iOS: map contentInset to UITextView.textContainerInset, and grow the caret-scroll target rect by the bottom inset so the caret stays revealed while typing.
  • Android: the ViewManager returns a NestedScrollView container whose child is the editor (wrap_content, non-scrolling). The inset is the editor's own padding — part of the scrolled content — so it scrolls away without clipping (Android TextView couples its draw clip to the padding while the field scrolls internally). The container scrolls and reveals the caret. Border/background/padding go on the container (the visible frame). A manual measure/layout pass and clipToOutline cover what RN does not here (it swallows the non-Fabric child's requestLayout, and RN parents default to overflow: visible). The shadow node stays an auto-grow measuring leaf, so consumers without an explicit height still grow to content.

Test plan

  • iOS and Android, contentInset with a long autofocused value: the cushion scrolls away (no clip) and autoFocus reveals the end.
  • Auto-grow (no explicit height) and maxHeight consumers still size correctly.
  • Existing props unaffected (border/background, scrollEnabled, selection menus, etc.).

@andreavrr

andreavrr commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Before / after

Before (paddingVertical clips) After (contentInset)
iOS
ios-before.mov
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-06-25.at.15.04.22.mov
Android
android-before.mov
android-after.mov

@andreavrr andreavrr marked this pull request as ready for review June 25, 2026 12:45

@hryhoriiK97 hryhoriiK97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@andreavrr thank you for this PR! It will require more consideration. I will look into this next week and let you know how we want to handle it.

@andreavrr

Copy link
Copy Markdown
Contributor Author

Thanks a lot @hryhoriiK97! I figured, there's probably still quite a bit to sort out. Either way it feels like an important feature (well, more of a fix) on the UI side. Thanks especially for giving me a sense of the timeline, happy to make any changes whenever you get to it.

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.

Feature request: contentInset prop for EnrichedMarkdownTextInput (non-clipping internal inset)

2 participants