Skip to content

[Vue] Page rendering overwrites component props declarations, removing defaults and validation #557

Closed

Description

@kotarotaniguchi0523

Problem

Void's Vue Pages adapter overwrites a page component's existing props declaration with the keys of the props received from the server.

This discards Vue's prop types, default values, required flags, and custom validators.

Additionally, the component's props declaration becomes fixed based on the first received props object, potentially affecting subsequent navigations.

Steps to Reproduce

  1. Create the following page component:
<script setup lang="ts">
defineProps({
  title: {
    type: String,
    default: 'DEFAULT',
  },
  count: {
    type: Number,
    required: true,
    validator: (value: number) => value > 0,
  },
});
</script>

<template>
  <p>title: {{ title }}</p>
  <p>count: {{ count }}</p>
</template>
  1. Render the page with title omitted and count: -1:
{
  component: 'Example',
  props: { count: -1 },
  url: '/example',
  params: {},
  shared: {},
  errors: null,
}
  1. Check the rendered output and Vue's validation warnings.

Actual Behavior

Both the generated SSR and client code contain the following implementation:

function patchProps(comp, props) {
  if (comp.__voidPatched) return;
  comp.props = Object.keys(props);
  comp.__voidPatched = true;
}

This replaces the original props declaration with:

comp.props = ['count'];

Consequently:

  • The title default value (DEFAULT) is lost.
  • The type, required flag, and custom validator for count are removed.
  • __voidPatched prevents the declaration from adapting to subsequent props objects.
  • Other usages of the same component definition may also be affected.

Root Cause

The Vue Pages adapter strips type arguments from defineProps<T>() and dynamically registers props based on the received page data.

However, patchProps() also runs on components with existing runtime props declarations.

Instead of supplementing missing declarations, it replaces the entire comp.props definition.

The __voidPatched flag then prevents subsequent updates to the registered prop names.

Expected Behavior

  • Preserve the component's original Vue props declaration.
  • Apply DEFAULT when title is omitted.
  • Emit a validation warning for count: -1 in development mode.
  • Preserve the same props contract across page navigations.

Acceptance Criteria

  • Preserve prop types, default values, required flags, and custom validators.
  • Do not derive a permanent props declaration from the first received props object.
  • Preserve props behavior across subsequent navigations.
  • Avoid mutating the original component definition.
  • Ensure consistent behavior in both SSR and client rendering.
  • Maintain compatibility with existing type-based defineProps<T>() declarations.

Relevant Code

packages/vue/src/plugin-pages.ts

https://github.com/voidzero-dev/void/blob/main/packages/vue/src/plugin-pages.ts

The affected patchProps() implementation is generated by both generateRendererModule() and generateClientEntry().

The problematic code path has been verified through source inspection. The reproduction above has not yet been executed as a runtime test.

Activity

  1. 0froq commented on Oct 26, 2026

    @0froq
    Collaborator

    Fix on the way

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions