Skip to content

Fix rx.code_block rendering empty when content is a state Var#6481

Closed
jryberg wants to merge 1 commit into
reflex-dev:mainfrom
jryberg:fix/6480-code-block-state-var-render
Closed

Fix rx.code_block rendering empty when content is a state Var#6481
jryberg wants to merge 1 commit into
reflex-dev:mainfrom
jryberg:fix/6480-code-block-state-var-render

Conversation

@jryberg
Copy link
Copy Markdown

@jryberg jryberg commented May 11, 2026

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Summary

Fixes #6480.

rx.code_block(state_var, language=..., show_line_numbers=True) renders an empty <pre><code class="language-yaml"> element. The state Var is populated at runtime — rx.set_clipboard(state_var) copies the full content, and rx.cond(state_var != "", ...) evaluates True — but SyntaxHighlighter receives nothing.

Root cause

CodeBlock._render() moves the user's first positional argument into props["children"]. For a stateful subtree, the compiler wraps the call in an auto-memo:

export const Codeblock_prismasynclight_<hash> = memo(({children}) => {
    const reflex___state__... = useContext(...)
    return jsx(SyntaxHighlighter, {children: state_var, ...}, children);
});

The third positional children is the wrapper's destructured prop, which is undefined when the wrapper is invoked with no children (always, for rx.code_block). @emotion/react's classic jsx delegates to React.createElement.apply(void 0, args), and React.createElement(type, props, undefined) has arguments.length === 3, so React sets props.children = undefined, overriding the legitimate value from the props object.

Fix

In _RenderUtils.render_tag (packages/reflex-base/src/reflex_base/compiler/templates.py), when props already declares a children: key, omit the positional children in the emitted jsx() call. Components that don't bind props.children keep their existing behaviour.

Tests

Two new tests in tests/units/compiler/test_compiler.py:

uv run pytest tests/units/compiler/ — 160 passed.

Checklist

  • Bug fix (non-breaking change which fixes an issue)
  • Tests pass: uv run pytest tests/units/compiler/test_compiler.py -k render_tag
  • Reproduced the bug end-to-end before the fix, and confirmed the fix renders syntax-highlighted content for rx.code_block(state_var, ...) across three independent call sites in a downstream application (Reflex 0.9.2 + the equivalent patch applied via post-install script).

When a component routes its content through `props.children` (notably
`rx.code_block` via `CodeBlock._render`, which moves the user's first
positional arg into `props["children"]`), the compiler was still
emitting positional children in the generated `jsx()` call. The
auto-memo wrapper produced for a stateful subtree destructures
`({children})` from its own (empty) props and passes that undefined
value as the third positional arg:

    jsx(SyntaxHighlighter, {children: state_var, ...}, children)

`@emotion/react`'s classic `jsx` delegates to
`React.createElement.apply(void 0, args)`, and
`React.createElement(type, props, undefined)` has `arguments.length === 3`,
so React sets `props.children = undefined` — overriding the legitimate
value. `SyntaxHighlighter` then renders the language-class wrapper with
no content.

Skip the positional-children emission in `_RenderUtils.render_tag`
whenever `props` already declares a `children:` key. Regular components
that do not bind `props.children` continue to receive their rendered
subtree positionally.

closes reflex-dev#6480
@jryberg jryberg requested a review from a team as a code owner May 11, 2026 07:33
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 11, 2026

Greptile Summary

This PR fixes a bug where rx.code_block rendered empty when its content was a state Var. The root cause was that _RenderUtils.render_tag emitted a positional children argument alongside props.children, and React's createElement let the positional arg (which is undefined inside an auto-memo wrapper) silently override the value already set in the props object.

  • templates.py: render_tag now detects when any prop string starts with "children:" and, in that case, omits positional children from the emitted jsx() call entirely, preserving the value already embedded in the props object.
  • test_compiler.py: Two new unit tests are added — one direct regression for the children:-in-props case and one guard for the normal positional-children path.

Confidence Score: 4/5

The change is narrowly scoped to JSX code-generation for components that route content through props.children; it fixes a real rendering bug without touching state management, event handling, or network code.

The fix is correct for the stated bug and the two new tests are well-targeted. The only open question is whether silently discarding rendered_children beyond the auto-memo placeholder could ever affect a future component — there is no assertion or warning to surface that case.

packages/reflex-base/src/reflex_base/compiler/templates.py — specifically the new early-return branch in render_tag and whether its silent-drop of positional children could affect any components beyond CodeBlock.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/compiler/templates.py render_tag now skips positional children when any prop starts with "children:"; logic is correct for the stated bug, but silently drops rendered_children in all cases — including if real children were present alongside children: in props.
tests/units/compiler/test_compiler.py Two well-written regression and guard tests added; they directly exercise the changed code path and protect the common positional-children path.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["render_tag(component)"] --> B["Extract props_list\nand rendered_children"]
    B --> C{"Any prop\nstartswith 'children:'?"}
    C -- Yes --> D["jsx(name, {props})\n(positional children dropped)"]
    C -- No --> E["jsx(name, {props}, child1, child2, ...)"]
    D --> F["React reads children\nfrom props object"]
    E --> G["React uses positional\nchildren as props.children"]
    style D fill:#90EE90
    style F fill:#90EE90
Loading

Reviews (1): Last reviewed commit: "Fix rx.code_block rendering empty when c..." | Re-trigger Greptile

Comment on lines +84 to +85
if any(p.startswith("children:") for p in props_list):
return f"jsx({name},{props})"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The guard correctly drops positional children when children: is already in props, but it does so unconditionally even if rendered_children contains real rendered elements beyond the auto-memo placeholder. If any future component's _render() places state content in props["children"] while also returning genuine child nodes in the children array, those nodes will be silently discarded with no diagnostic. A lightweight check here makes the intent explicit and prevents a hard-to-debug silent regression.

Suggested change
if any(p.startswith("children:") for p in props_list):
return f"jsx({name},{props})"
if any(p.startswith("children:") for p in props_list):
# rendered_children should only contain the auto-memo placeholder
# ("children") when this path is taken; log a warning if real
# elements were also present so the mismatch is not silently lost.
non_placeholder = [c for c in rendered_children if c != "children"]
if non_placeholder:
import warnings
warnings.warn(
f"render_tag: component '{name}' declares children in props "
f"but also has {len(non_placeholder)} positional child(ren) "
"that will be dropped. This is likely a bug in _render().",
stacklevel=2,
)
return f"jsx({name},{props})"

@FarhanAliRaza
Copy link
Copy Markdown
Contributor

I think this was already fixed here #6466

@masenf
Copy link
Copy Markdown
Collaborator

masenf commented May 11, 2026

since we already have a more general fix for this in main (unreleased as of now), but the fix didn't include any kind of regression test, we probably should add an integration test for code block component that asserts on functionality: theme, copy button, language, content and these props variously coming statically or from state vars.

@masenf
Copy link
Copy Markdown
Collaborator

masenf commented May 11, 2026

We have released the fix for this in 0.9.2.post1, please upgrade and confirm that the issue you were seeing is resolved.

I filed #6484 as an enhancement to add the true integration tests for code_block component.

Will close this PR now, but we appreciate your involvement in the project 😌

@masenf masenf closed this May 11, 2026
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.

rx.code_block renders empty when content is a state Var (0.9.2)

3 participants