diff --git a/packages/nuxt/src/app/components/client-only.ts b/packages/nuxt/src/app/components/client-only.ts index a24c0864b9..796ccc5858 100644 --- a/packages/nuxt/src/app/components/client-only.ts +++ b/packages/nuxt/src/app/components/client-only.ts @@ -55,15 +55,18 @@ export function createClientOnly (component: T) { clone.setup = (props, ctx) => { const instance = getCurrentInstance()! - const attrs = instance.attrs + const attrs = { ...instance.attrs } + // remove existing directives during hydration const directives = extractDirectives(instance) // prevent attrs inheritance since a staticVNode is rendered before hydration - instance.attrs = {} + for(const key in attrs) { + delete instance.attrs[key] + } const mounted$ = ref(false) onMounted(() => { - instance.attrs = attrs + Object.assign(instance.attrs, attrs) instance.vnode.dirs = directives mounted$.value = true }) diff --git a/test/nuxt/client.test.ts b/test/nuxt/client.test.ts new file mode 100644 index 0000000000..5bd8def01b --- /dev/null +++ b/test/nuxt/client.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest' +import type { ComponentOptions } from 'vue' +import { defineComponent, h, toDisplayString, useAttrs } from 'vue' +import { mountSuspended } from '@nuxt/test-utils/runtime' +import { createClientOnly } from '../../packages/nuxt/src/app/components/client-only' + +const Client = defineComponent({ + name: 'TestClient', + setup () { + const attrs = useAttrs() + return () => h('div', {}, toDisplayString(attrs)) + } +}) + +describe('createClient attribute inheritance', () => { + it('should retrieve attributes with useAttrs()', async () => { + const wrapper = await mountSuspended(createClientOnly(Client as ComponentOptions), { + attrs: { + id: 'client' + } + }) + + expect(wrapper.html()).toMatchInlineSnapshot(` + "
{ + "id": "client" + }
" + `) + }) +})