Skip to content

fix: update string input value in real-time#603

Open
dinakars777 wants to merge 4 commits intopmndrs:mainfrom
dinakars777:fix/string-input-realtime-update
Open

fix: update string input value in real-time#603
dinakars777 wants to merge 4 commits intopmndrs:mainfrom
dinakars777:fix/string-input-realtime-update

Conversation

@dinakars777
Copy link
Copy Markdown

@dinakars777 dinakars777 commented Mar 16, 2026

Summary

Previously, string inputs in Leva only committed changes to the store when the input lost focus (on blur). This meant that useEffect hooks depending on the value would only fire after clicking away from the input.

This fix makes onUpdate fire on every keystroke, so the store value updates in real-time.

Changes

  • Modified ValueInput.tsx to call both onChange and onUpdate on every onChange event
  • This affects both text inputs and textareas

Testing

Build passes successfully. The change maintains backward compatibility - onUpdate was already being called on blur, and now it's also called on every keystroke.

Fixes #599

Summary by CodeRabbit

  • Bug Fixes
    • String/text inputs now propagate changes in real time as you type when editable, ensuring external listeners and UI reflect edits immediately; numeric inputs still commit on blur/Enter to preserve existing behavior.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 16, 2026

🦋 Changeset detected

Latest commit: 2d84cdc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
leva Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 16, 2026

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

Project Deployment Actions Updated (UTC)
leva Ready Ready Preview, Comment Mar 23, 2026 4:56am

Request Review

@codesandbox-ci
Copy link
Copy Markdown

codesandbox-ci bot commented Mar 16, 2026

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 2d84cdc:

Sandbox Source
leva-minimal Configuration
leva-busy Configuration
leva-scroll Configuration
leva-advanced-panels Configuration
leva-ui Configuration
leva-theme Configuration
leva-transient Configuration
leva-plugin-plot Configuration
leva-plugin-bezier Configuration
leva-plugin-spring Configuration
leva-plugin-dates Configuration
leva-custom-plugin Configuration

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

ValueInput and its props now accept an optional liveUpdate?: boolean. StyledInput's onChange always calls onChange(value) and conditionally calls onUpdate(value) during typing for non-number inputs or when liveUpdate is true. String forwards liveUpdate when editable is true. A changeset was added.

Changes

Cohort / File(s) Summary
ValueInput component
packages/leva/src/components/ValueInput/ValueInput.tsx
Added optional liveUpdate?: boolean to ValueInputProps; StyledInput's onChange now calls onChange(value) and conditionally invokes onUpdate(value) inline for non-number types or when liveUpdate is true. Blur/Enter and number commit paths unchanged.
String plugin
packages/leva/src/plugins/String/String.tsx
When editable is true, String now forwards the liveUpdate prop to ValueInput. No behavior change when editable is false.
Release metadata
.changeset/plenty-spies-provide.md
Added a changeset marking leva for a patch release with note: “fix: update string input value in real-time”.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I nibble keys and watch them flow,
Each letter leaps where pixels go.
No waiting til I hop away,
Live updates hum throughout the day.
Tiny rabbit, bright hooray! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: enabling real-time updates for string input values.
Linked Issues check ✅ Passed The PR successfully implements the objective from issue #599 by modifying ValueInput.tsx to call onUpdate on every keystroke for non-number inputs, enabling string values to propagate to store immediately.
Out of Scope Changes check ✅ Passed All changes are focused on the real-time string input update objective: ValueInput.tsx adds liveUpdate prop logic, String.tsx forwards it, and the changeset documents the fix. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/leva/src/components/ValueInput/ValueInput.tsx`:
- Around line 87-90: The current onChange handler in ValueInput unconditionally
calls onUpdate(value) on every keystroke which causes NumberInput (which reuses
ValueInput) to also commit on each keystroke; change the handler in ValueInput
so it only calls onUpdate for string inputs (e.g., check props.inputType !==
'number' or typeof value === 'string') and leave numeric inputs to retain their
original commit semantics; update the onChange={(value) => { onChange(value); if
(<guard-for-non-number-or-string>) onUpdate(value); }} logic so NumberInput
behavior is unchanged while string realtime updates remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 148ee752-814f-4729-b100-0ebea143c9bd

📥 Commits

Reviewing files that changed from the base of the PR and between 402a1e9 and 8470ecd.

📒 Files selected for processing (1)
  • packages/leva/src/components/ValueInput/ValueInput.tsx

Copy link
Copy Markdown

@travisbreaks travisbreaks left a comment

Choose a reason for hiding this comment

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

The change is small and targeted, but the behavioral implications are worth discussing.

Performance concern
Calling onUpdate on every keystroke means the leva store updates on each character typed. If downstream consumers have expensive side effects (e.g., shader recompilation, network requests, scene updates in R3F), this could cause noticeable jank. The previous blur-on-commit pattern existed for a reason.

Possible alternatives:

  1. Debounced onUpdate: Fire onUpdate after a short delay (e.g., 150ms) since the last keystroke. This gives real-time feel without per-character store updates.
  2. Opt-in behavior: Add a liveUpdate option to string inputs so consumers can choose between immediate and commit-on-blur.

Undo/redo
If leva tracks history for undo, each keystroke now creates a history entry. Typing "hello" would produce 5 undo steps instead of 1. Is that the intended behavior?

The type !== 'number' guard
Makes sense since number inputs have their own drag/scrub update logic. But worth a comment explaining why numbers are excluded.

The fix solves a real usability issue (string inputs feeling "dead" until blur), but the implementation could benefit from a debounce to avoid downstream performance surprises.

Previously, string inputs only committed changes to the store on blur.
This change makes onUpdate fire on every keystroke for string inputs
only - number inputs retain their original commit behavior (on blur/enter).

Added comment explaining the type !== 'number' guard.

Fixes pmndrs#599
@dinakars777 dinakars777 force-pushed the fix/string-input-realtime-update branch from 5b335d2 to 963cf15 Compare March 19, 2026 07:45
@dinakars777
Copy link
Copy Markdown
Author

Thanks for the review @travisbreaks!

The concerns are valid. Here's my thinking:

  1. Performance/Debounce: The current fix directly addresses the exact issue reported (String input value only changes when losing focus #599) - the user expected useEffect to fire on every keystroke. A debounced approach would technically not fulfill that requirement (150ms delay ≠ "every time it changes"). A follow-up enhancement for debounced updates could be added later if needed.

  2. Undo/Redo: This is a valid concern, but Leva's current architecture uses onChange for rendering and onUpdate for committing - consumers already deal with this distinction.

  3. Guard comment: Added a comment explaining the type !== 'number' guard.

The type !== 'number' guard already ensures number inputs keep their original behavior - only string/text inputs get the real-time update.

- Only call onUpdate for non-number inputs during realtime updates
- Add liveUpdate prop for explicit opt-in (for String plugin)
- Number inputs retain original commit behavior (on blur/Enter)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/leva/src/plugins/String/String.tsx (1)

16-16: Ensure liveUpdate cannot be accidentally overridden

On Line 16, liveUpdate is set before {...props}, so props.liveUpdate={false} would win and reintroduce blur-only commits. If editable strings must always update live, place liveUpdate last.

Proposed fix
-  if (editable) return <ValueInput value={displayValue} onUpdate={onUpdate} onChange={onChange} liveUpdate {...props} />
+  if (editable) return <ValueInput value={displayValue} onUpdate={onUpdate} onChange={onChange} {...props} liveUpdate />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/leva/src/plugins/String/String.tsx` at line 16, The editable string
branch currently renders ValueInput with liveUpdate before spreading {...props},
so a passed prop like props.liveUpdate=false can override it; in the editable
branch (the conditional that returns <ValueInput ... />) ensure liveUpdate is
placed after the spread (or move the spread before and then append liveUpdate)
so that ValueInput(..., {...props}, liveUpdate) always forces live
updates—update the JSX rendering of ValueInput in the editable branch
(referencing ValueInput, displayValue, onUpdate, onChange, and props)
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/leva/src/plugins/String/String.tsx`:
- Line 16: The editable string branch currently renders ValueInput with
liveUpdate before spreading {...props}, so a passed prop like
props.liveUpdate=false can override it; in the editable branch (the conditional
that returns <ValueInput ... />) ensure liveUpdate is placed after the spread
(or move the spread before and then append liveUpdate) so that ValueInput(...,
{...props}, liveUpdate) always forces live updates—update the JSX rendering of
ValueInput in the editable branch (referencing ValueInput, displayValue,
onUpdate, onChange, and props) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d0bb799-0f8f-4c34-be7c-18adfc35e218

📥 Commits

Reviewing files that changed from the base of the PR and between 38a01ed and 85943b3.

📒 Files selected for processing (2)
  • packages/leva/src/components/ValueInput/ValueInput.tsx
  • packages/leva/src/plugins/String/String.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/leva/src/components/ValueInput/ValueInput.tsx

@dinakars777
Copy link
Copy Markdown
Author

Thanks for the review! Fixed by adding liveUpdate prop with guard for non-number inputs - only calls onUpdate when type !== 'number' || liveUpdate. Resolved.

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.

String input value only changes when losing focus

2 participants