diff --git a/docs/content/2.guide/2.directory-structure/1.components.md b/docs/content/2.guide/2.directory-structure/1.components.md
index 8bac9fa14e9..d85986fac23 100644
--- a/docs/content/2.guide/2.directory-structure/1.components.md
+++ b/docs/content/2.guide/2.directory-structure/1.components.md
@@ -201,7 +201,7 @@ If a component is meant to be rendered only client-side, you can add the `.clien
 ```
 
 ::alert{type=warning}
-This feature only works with Nuxt auto-imports. Explicitly importing these components does not convert them into client-only components.
+This feature only works with Nuxt auto-imports and `#components` imports. Explicitly importing these components from their real paths does not convert them into client-only components.
 ::
 
 ## .server Components
diff --git a/packages/nuxt/src/components/module.ts b/packages/nuxt/src/components/module.ts
index 891617d6543..b7fc286119f 100644
--- a/packages/nuxt/src/components/module.ts
+++ b/packages/nuxt/src/components/module.ts
@@ -2,6 +2,7 @@ import { statSync } from 'node:fs'
 import { relative, resolve } from 'pathe'
 import { defineNuxtModule, resolveAlias, addTemplate, addPluginTemplate } from '@nuxt/kit'
 import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema'
+import { distDir } from '../dirs'
 import { componentsPluginTemplate, componentsTemplate, componentsTypeTemplate } from './templates'
 import { scanComponents } from './scan'
 import { loaderPlugin } from './loader'
@@ -146,6 +147,17 @@ export default defineNuxtModule<ComponentsOptions>({
     nuxt.hook('app:templates', async () => {
       const newComponents = await scanComponents(componentDirs, nuxt.options.srcDir!)
       await nuxt.callHook('components:extend', newComponents)
+      // add server placeholder for .client components server side. issue: #7085
+      for (const component of newComponents) {
+        if (component.mode === 'client' && !newComponents.some(c => c.pascalName === component.pascalName && c.mode === 'server')) {
+          newComponents.push({
+            ...component,
+            mode: 'server',
+            filePath: resolve(distDir, 'app/components/server-placeholder'),
+            chunkName: 'components/' + component.kebabName
+          })
+        }
+      }
       context.components = newComponents
     })
 
diff --git a/packages/nuxt/src/components/templates.ts b/packages/nuxt/src/components/templates.ts
index bf085cb0be3..00b3356dbfc 100644
--- a/packages/nuxt/src/components/templates.ts
+++ b/packages/nuxt/src/components/templates.ts
@@ -1,6 +1,6 @@
 import { isAbsolute, relative } from 'pathe'
 import type { Component, Nuxt, NuxtPluginTemplate, NuxtTemplate } from '@nuxt/schema'
-import { genDynamicImport, genExport, genObjectFromRawEntries } from 'knitwork'
+import { genDynamicImport, genExport, genImport, genObjectFromRawEntries } from 'knitwork'
 
 export interface ComponentsTemplateContext {
   nuxt: Nuxt
@@ -53,17 +53,31 @@ export default defineNuxtPlugin(nuxtApp => {
 export const componentsTemplate: NuxtTemplate<ComponentsTemplateContext> = {
   // components.[server|client].mjs'
   getContents ({ options }) {
-    return [
-      'import { defineAsyncComponent } from \'vue\'',
-      ...options.getComponents(options.mode).flatMap((c) => {
-        const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']`
-        const comment = createImportMagicComments(c)
+    const imports = new Set<string>()
+    imports.add('import { defineAsyncComponent } from \'vue\'')
+
+    let num = 0
+    const components = options.getComponents(options.mode).flatMap((c) => {
+      const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']`
+      const comment = createImportMagicComments(c)
 
-        return [
-          genExport(c.filePath, [{ name: c.export, as: c.pascalName }]),
-          `export const Lazy${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))`
-        ]
-      }),
+      const isClient = c.mode === 'client'
+      const definitions = []
+      if (isClient) {
+        num++
+        const identifier = `__nuxt_component_${num}`
+        imports.add(genImport('#app/components/client-only', [{ name: 'createClientOnly' }]))
+        imports.add(genImport(c.filePath, [{ name: c.export, as: identifier }]))
+        definitions.push(`export const ${c.pascalName} =  /*#__PURE__*/  createClientOnly(${identifier})`)
+      } else {
+        definitions.push(genExport(c.filePath, [{ name: c.export, as: c.pascalName }]))
+      }
+      definitions.push(`export const Lazy${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${isClient ? `createClientOnly(${exp})` : exp}))`)
+      return definitions
+    })
+    return [
+      ...imports,
+      ...components,
       `export const componentNames = ${JSON.stringify(options.getComponents().map(c => c.pascalName))}`
     ].join('\n')
   }
diff --git a/test/basic.test.ts b/test/basic.test.ts
index c288fc1244c..cc964d76fce 100644
--- a/test/basic.test.ts
+++ b/test/basic.test.ts
@@ -219,6 +219,17 @@ describe('pages', () => {
     await Promise.all(hiddenSelectors.map(selector => page.locator(selector).isVisible()))
       .then(results => results.forEach(isVisible => expect(isVisible).toBeTruthy()))
   })
+
+  it('/client-only-explicit-import', async () => {
+    const html = await $fetch('/client-only-explicit-import')
+
+    // ensure fallbacks with classes and arbitrary attributes are rendered
+    expect(html).toContain('<div class="client-only-script" foo="bar">')
+    expect(html).toContain('<div class="lazy-client-only-script-setup" foo="hello">')
+    // ensure components are not rendered server-side
+    expect(html).not.toContain('client only script')
+    await expectNoClientErrors('/client-only-components')
+  })
 })
 
 describe('head tags', () => {
diff --git a/test/fixtures/basic/components/BreaksServer.ts b/test/fixtures/basic/components/BreaksServer.client.ts
similarity index 100%
rename from test/fixtures/basic/components/BreaksServer.ts
rename to test/fixtures/basic/components/BreaksServer.client.ts
diff --git a/test/fixtures/basic/pages/client-only-explicit-import.vue b/test/fixtures/basic/pages/client-only-explicit-import.vue
new file mode 100644
index 00000000000..23463cd4019
--- /dev/null
+++ b/test/fixtures/basic/pages/client-only-explicit-import.vue
@@ -0,0 +1,11 @@
+
+<template>
+  <div>
+    <ClientOnlyScript class="client-only-script" foo="bar" />
+    <LazyClientOnlySetupScript class="lazy-client-only-script-setup" foo="hello" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ClientOnlyScript, LazyClientOnlySetupScript } from '#components'
+</script>
diff --git a/test/fixtures/basic/pages/client.vue b/test/fixtures/basic/pages/client.vue
index 95da86f78d4..682d0272d28 100644
--- a/test/fixtures/basic/pages/client.vue
+++ b/test/fixtures/basic/pages/client.vue
@@ -1,12 +1,15 @@
 <script setup lang="ts">
-onMounted(() => import('~/components/BreaksServer'))
-onBeforeMount(() => import('~/components/BreaksServer'))
-onBeforeUpdate(() => import('~/components/BreaksServer'))
-onRenderTracked(() => import('~/components/BreaksServer'))
-onRenderTriggered(() => import('~/components/BreaksServer'))
-onActivated(() => import('~/components/BreaksServer'))
-onDeactivated(() => import('~/components/BreaksServer'))
-onBeforeUnmount(() => import('~/components/BreaksServer'))
+// explicit import to bypass client import protection
+import BreaksServer from '../components/BreaksServer.client'
+
+onMounted(() => import('~/components/BreaksServer.client'))
+onBeforeMount(() => import('~/components/BreaksServer.client'))
+onBeforeUpdate(() => import('~/components/BreaksServer.client'))
+onRenderTracked(() => import('~/components/BreaksServer.client'))
+onRenderTriggered(() => import('~/components/BreaksServer.client'))
+onActivated(() => import('~/components/BreaksServer.client'))
+onDeactivated(() => import('~/components/BreaksServer.client'))
+onBeforeUnmount(() => import('~/components/BreaksServer.client'))
 </script>
 
 <template>