Skip to content

Commit

Permalink
feat(nuxt): enable islands if server pages/components present (#26223)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed Mar 13, 2024
1 parent f080c42 commit 9bfd988
Show file tree
Hide file tree
Showing 9 changed files with 20 additions and 16 deletions.
4 changes: 0 additions & 4 deletions docs/2.guide/2.directory-structure/1.pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,6 @@ You can define a page as [client only](/docs/guide/directory-structure/component

You can define a page as [server only](/docs/guide/directory-structure/components#server-components) by giving it a `.server.vue` suffix. While you will be able to navigate to the page using client-side navigation, controlled by `vue-router`, it will be rendered with a server component automatically, meaning the code required to render the page will not be in your client-side bundle.

::note
You will also need to enable `experimental.componentIslands` in order to make this possible.
::

## Custom Routing

As your app gets bigger and more complex, your routing might require more flexibility. For this reason, Nuxt directly exposes the router, routes and router options for customization in different ways.
Expand Down
4 changes: 0 additions & 4 deletions docs/3.api/1.components/8.nuxt-island.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ When rendering an island component, the content of the island component is stati

Changing the island component props triggers a refetch of the island component to re-render it again.

::read-more{to="/docs/guide/going-further/experimental-features#componentislands" icon="i-ph-star-duotone"}
This component is experimental and in order to use it you must enable the `experimental.componentIslands` option in your `nuxt.config`.
::

::note
Global styles of your application are sent with the response.
::
Expand Down
3 changes: 3 additions & 0 deletions packages/nuxt/src/components/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ export default defineNuxtModule<ComponentsOptions>({
chunkName: 'components/' + component.kebabName
})
}
if (component.mode === 'server' && !nuxt.options.ssr) {
logger.warn(`Using server components with \`ssr: false\` is not supported with auto-detected component islands. If you need to use server component \`${component.pascalName}\`, set \`experimental.componentIslands\` to \`true\`.`)
}
}
context.components = newComponents
app.components = newComponents
Expand Down
1 change: 0 additions & 1 deletion packages/nuxt/src/core/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
'process.env.NUXT_NO_SCRIPTS': !!nuxt.options.features.noScripts && !nuxt.options.dev,
'process.env.NUXT_INLINE_STYLES': !!nuxt.options.features.inlineStyles,
'process.env.NUXT_JSON_PAYLOADS': !!nuxt.options.experimental.renderJsonPayloads,
'process.env.NUXT_COMPONENT_ISLANDS': !!nuxt.options.experimental.componentIslands,
'process.env.NUXT_ASYNC_CONTEXT': !!nuxt.options.experimental.asyncContext,
'process.env.NUXT_SHARED_DATA': !!nuxt.options.experimental.sharedPrerenderData,
'process.dev': nuxt.options.dev,
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/src/core/nuxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ async function initNuxt (nuxt: Nuxt) {
filePath: resolve(nuxt.options.appDir, 'components/nuxt-island')
})

if (!nuxt.options.ssr) {
if (!nuxt.options.ssr && nuxt.options.experimental.componentIslands !== 'auto') {
nuxt.options.ssr = true
nuxt.options.nitro.routeRules ||= {}
nuxt.options.nitro.routeRules['/**'] = defu(nuxt.options.nitro.routeRules['/**'], { ssr: false })
Expand Down
6 changes: 3 additions & 3 deletions packages/nuxt/src/core/runtime/nitro/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import unheadPlugins from '#internal/unhead-plugins.mjs'
// eslint-disable-next-line import/no-restricted-paths
import type { NuxtPayload, NuxtSSRContext } from '#app'
// @ts-expect-error virtual file
import { appHead, appRootId, appRootTag, appTeleportId, appTeleportTag } from '#internal/nuxt.config.mjs'
import { appHead, appRootId, appRootTag, appTeleportId, appTeleportTag, componentIslands } from '#internal/nuxt.config.mjs'
// @ts-expect-error virtual file
import { buildAssetsURL, publicAssetsURL } from '#paths'

Expand Down Expand Up @@ -263,7 +263,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
}

// Check for island component rendering
const isRenderingIsland = (process.env.NUXT_COMPONENT_ISLANDS as unknown as boolean && event.path.startsWith('/__nuxt_island'))
const isRenderingIsland = (componentIslands as unknown as boolean && event.path.startsWith('/__nuxt_island'))
const islandContext = isRenderingIsland ? await getIslandContext(event) : undefined

if (import.meta.prerender && islandContext && event.path && await islandCache!.hasItem(event.path)) {
Expand Down Expand Up @@ -468,7 +468,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
bodyAttrs: bodyAttrs ? [bodyAttrs] : [],
bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]),
body: [
process.env.NUXT_COMPONENT_ISLANDS ? replaceIslandTeleports(ssrContext, _rendered.html) : _rendered.html,
componentIslands ? replaceIslandTeleports(ssrContext, _rendered.html) : _rendered.html,
APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportId}`]]) : '') + APP_TELEPORT_CLOSE_TAG
],
bodyAppend: [bodyTags]
Expand Down
5 changes: 4 additions & 1 deletion packages/nuxt/src/core/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,10 +374,13 @@ export const nuxtConfigTemplate: NuxtTemplate = {
baseURL: undefined,
headers: undefined
}
const shouldEnableComponentIslands = ctx.nuxt.options.experimental.componentIslands && (
ctx.nuxt.options.dev || ctx.nuxt.options.experimental.componentIslands !== 'auto' || ctx.app.pages?.some(p => p.mode === 'server') || ctx.app.components?.some(c => c.mode === 'server')
)
return [
...Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase('app-' + k)} = ${JSON.stringify(v)}`),
`export const renderJsonPayloads = ${!!ctx.nuxt.options.experimental.renderJsonPayloads}`,
`export const componentIslands = ${!!ctx.nuxt.options.experimental.componentIslands}`,
`export const componentIslands = ${shouldEnableComponentIslands}`,
`export const payloadExtraction = ${!!ctx.nuxt.options.experimental.payloadExtraction}`,
`export const cookieStore = ${!!ctx.nuxt.options.experimental.cookieStore}`,
`export const appManifest = ${!!ctx.nuxt.options.experimental.appManifest}`,
Expand Down
4 changes: 4 additions & 0 deletions packages/nuxt/src/pages/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export default defineNuxtModule({
nuxt.hook('app:templates', async (app) => {
app.pages = await resolvePagesRoutes()
await nuxt.callHook('pages:extend', app.pages)

if (!nuxt.options.ssr && app.pages.some(p => p.mode === 'server')) {
logger.warn('Using server pages with `ssr: false` is not supported with auto-detected component islands. Set `experimental.componentIslands` to `true`.')
}
})

// Restart Nuxt when pages dir is added or removed
Expand Down
7 changes: 5 additions & 2 deletions packages/schema/src/config/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ export default defineUntypedSchema({

/**
* Experimental component islands support with <NuxtIsland> and .island.vue files.
* @type {true | 'local' | 'local+remote' | Partial<{ remoteIsland: boolean, selectiveClient: boolean }> | false}
*
* By default it is set to 'auto', which means it will be enabled only when there are islands,
* server components or server pages in your app.
* @type {true | 'auto' | 'local' | 'local+remote' | Partial<{ remoteIsland: boolean, selectiveClient: boolean }> | false}
*/
componentIslands: {
$resolve: (val) => {
Expand All @@ -182,7 +185,7 @@ export default defineUntypedSchema({
if (val === 'local') {
return true
}
return val ?? false
return val ?? 'auto'
}
},

Expand Down

0 comments on commit 9bfd988

Please sign in to comment.