Repository: nuxt/nuxt
File: packages/nuxt/src/app/components/client-only.ts (TypeScript)

Code before:

const STATIC_DIV = '<div></div>'

export default defineComponent({
  name: 'ClientOnly',
  inheritAttrs: false,
  props: ['fallback', 'placeholder', 'placeholderTag', 'fallbackTag'],
  setup (props, { slots, attrs }) {
    const mounted = ref(false)
    onMounted(() => { mounted.value = true })
    // Bail out of checking for pages/layouts as they might be included under `<ClientOnly>` 🤷‍♂️
    if (import.meta.dev) {
      const nuxtApp = useNuxtApp()
      nuxtApp._isNuxtPageUsed = true
      nuxtApp._isNuxtLayoutUsed = true
    }
    const vm = getCurrentInstance()
    if (vm) {
      vm._nuxtClientOnly = true
    }
    provide(clientOnlySymbol, true)
    return () => {
      if (mounted.value) {
        const vnodes = slots.default?.()
        if (vnodes && vnodes.length === 1) {
          return h(vnodes[0]!, attrs)
        }
        return vnodes
      }
      const slot = slots.fallback || slots.placeholder
      if (slot) { return h(slot) }
      const fallbackStr = props.fallback || props.placeholder || ''
      const fallbackTag = props.fallbackTag || props.placeholderTag || 'span'
      return createElementBlock(fallbackTag, attrs, fallbackStr)
    }
  },
})

const cache = new WeakMap()

/* @__NO_SIDE_EFFECTS__ */
export function createClientOnly<T extends ComponentOptions> (component: T) {
  if (import.meta.server) {
    return ServerPlaceholder
  }
  if (cache.has(component)) {
    return cache.get(component)
  }

  const clone = { ...component }


Review comment (suggestion):
Using `h` to pass attributes onto an existing vnode is not supported; use `cloneVNode(vnodes[0], attrs)` to properly merge props and attributes onto the child VNode.
```suggestion
          return cloneVNode(vnodes[0]!, attrs)
```

Code after:

const STATIC_DIV = '<div></div>'

export default defineComponent({
  name: 'ClientOnly',
  inheritAttrs: false,
  props: ['fallback', 'placeholder', 'placeholderTag', 'fallbackTag'],
  setup (props, { slots, attrs }) {
    const mounted = ref(false)
    onMounted(() => { mounted.value = true })
    // Bail out of checking for pages/layouts as they might be included under `<ClientOnly>` 🤷‍♂️
    if (import.meta.dev) {
      const nuxtApp = useNuxtApp()
      nuxtApp._isNuxtPageUsed = true
      nuxtApp._isNuxtLayoutUsed = true
    }
    const vm = getCurrentInstance()
    if (vm) {
      vm._nuxtClientOnly = true
    }
    provide(clientOnlySymbol, true)
    return () => {
      if (mounted.value) {
        const vnodes = slots.default?.()
        if (vnodes && vnodes.length === 1) {
          return [cloneVNode(vnodes[0]!, attrs)]
        }
        return vnodes
      }
      const slot = slots.fallback || slots.placeholder
      if (slot) { return h(slot) }
      const fallbackStr = props.fallback || props.placeholder || ''
      const fallbackTag = props.fallbackTag || props.placeholderTag || 'span'
      return createElementBlock(fallbackTag, attrs, fallbackStr)
    }
  },
})

const cache = new WeakMap()

/* @__NO_SIDE_EFFECTS__ */
export function createClientOnly<T extends ComponentOptions> (component: T) {
  if (import.meta.server) {
    return ServerPlaceholder
  }
  if (cache.has(component)) {
    return cache.get(component)
  }

  const clone = { ...component }