vuejs/vue · error · Error
Content placeholder not found in template.
Error message
Content placeholder not found in template.
What it means
Thrown by parseTemplate when the template HTML does not contain the content placeholder (default: <!--vue-ssr-outlet-->). The placeholder marks where rendered app content is injected between neck and tail. Without it the template cannot be split into head/neck/tail sections. parseTemplate is called once during TemplateRenderer construction when a string template is provided.
Source
Thrown at packages/server-renderer/src/template-renderer/parse-template.ts:25
export type ParsedTemplate = {
head: (data: any) => string
neck: (data: any) => string
tail: (data: any) => string
}
export function parseTemplate(
template: string,
contentPlaceholder: string = '<!--vue-ssr-outlet-->'
): ParsedTemplate {
if (typeof template === 'object') {
return template
}
let i = template.indexOf('</head>')
const j = template.indexOf(contentPlaceholder)
if (j < 0) {
throw new Error(`Content placeholder not found in template.`)
}
if (i < 0) {
i = template.indexOf('<body>')
if (i < 0) {
i = j
}
}
return {
head: compile(template.slice(0, i), compileOptions),
neck: compile(template.slice(i, j), compileOptions),
tail: compile(template.slice(j + contentPlaceholder.length), compileOptions)
}
}
View on GitHub (pinned to 9e88707940)
Solutions
- Add <!--vue-ssr-outlet--> inside the <body> of the template HTML where app content should appear.
- If using a custom placeholder, pass it as the contentPlaceholder argument to parseTemplate (note: createRenderer does not expose this, so the default marker is effectively required).
- Ensure HTML minifiers preserve comments or run after the template is parsed.
- If the template is an object/function (programmatic), parseTemplate returns it as-is and this check is skipped.
Example fix
<!-- before -->
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
</body>
</html>
<!-- after -->
<!DOCTYPE html>
<html>
<body>
<!--vue-ssr-outlet-->
</body>
</html> Defensive patterns
Strategy: validation
Validate before calling
const PLACEHOLDER = '<!--vue-ssr-outlet-->'
function templateHasPlaceholder(template: string, placeholder = PLACEHOLDER): boolean {
return template.indexOf(placeholder) >= 0
}
if (typeof options.template === 'string' && !templateHasPlaceholder(options.template)) {
throw new Error(
`SSR template must contain ${PLACEHOLDER} where app content should be injected.`
)
}
createRenderer(options) Type guard
function isValidSsrTemplate(t: unknown): boolean {
return typeof t === 'string' && t.includes('<!--vue-ssr-outlet-->')
} Try / catch
try {
createRenderer({ template })
} catch (e) {
if (e.message.includes('Content placeholder not found')) {
template = template.replace('<div id="app"></div>', '<!--vue-ssr-outlet-->')
createRenderer({ template })
} else {
throw e
}
} Prevention
- Always include <!--vue-ssr-outlet--> in the SSR template HTML inside <body>.
- Configure HTML minifiers to preserve comments (html-minifier: collapseWhitespace with removeComments=false).
- Store the template as a checked constant rather than inline string to avoid drift.
- Add a startup test that asserts the template contains the placeholder.
When it happens
Trigger: Passing createRenderer a template string that lacks the <!--vue-ssr-outlet--> marker (or whatever custom contentPlaceholder was configured). The indexOf check returns -1 and the error fires immediately at renderer creation.
Common situations: Copy-pasting an HTML shell from a non-Vue project that omits the placeholder. Renaming or removing the placeholder by mistake. Using {{{app}}} or {{content}} (other frameworks' syntax) instead of the Vue SSR marker. A minifier or HTML processor stripped HTML comments including the placeholder.
Related errors
- function template is only supported in renderToString.
- \n[31m${err}${trace}[39m\n
- render function or template not defined in component: ${vm.$
- render cannot be called without a template.
- createStream cannot be called without a template.
AI-assisted analysis of vuejs/vue@9e88707940 (2026-08-11).
Data as JSON: /api/errors/68c864ba2d940cae.
Report an issue: GitHub.