Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.3 KiB
5.3 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Implement getSSRProps for Custom Directives in SSR | MEDIUM | Custom directives without SSR handling cause hydration mismatches or missing functionality | best-practice |
|
Implement getSSRProps for Custom Directives in SSR
Impact: MEDIUM - Custom directives only have access to the DOM on the client side. During SSR, the directive's mounted and updated hooks never run. If your directive sets attributes or modifies the element, you must implement getSSRProps to return equivalent attributes for server rendering.
Without getSSRProps, the server-rendered HTML won't include the directive's effects, causing hydration mismatches when the client applies the directive.
Task Checklist
- Add
getSSRPropshook to directives that modify element attributes - Return an object with HTML attributes to render on server
- Test directive behavior in both SSR and client-only contexts
- Consider using components instead of directives for complex SSR cases
Incorrect - Client-Only Directive:
// WRONG: No SSR handling - directive effects missing on server
const vTooltip = {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.setAttribute('aria-label', binding.value)
el.classList.add('has-tooltip')
}
}
Server renders:
<!-- Missing data-tooltip, aria-label, and has-tooltip class -->
<button>Hover me</button>
Client after hydration:
<!-- Directive applies, but causes mismatch -->
<button data-tooltip="Help text" aria-label="Help text" class="has-tooltip">
Hover me
</button>
Correct - With getSSRProps:
// CORRECT: SSR-compatible directive
const vTooltip = {
// Client-side implementation
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.setAttribute('aria-label', binding.value)
el.classList.add('has-tooltip')
},
// SSR implementation - returns attributes to render
getSSRProps(binding) {
return {
'data-tooltip': binding.value,
'aria-label': binding.value,
class: 'has-tooltip'
}
}
}
Server now renders:
<button data-tooltip="Help text" aria-label="Help text" class="has-tooltip">
Hover me
</button>
Complete SSR Directive Example
// directives/vFocus.js
export const vFocus = {
// Client: Actually focus the element
mounted(el, binding) {
if (binding.value !== false) {
el.focus()
}
},
// SSR: Add autofocus attribute so browser focuses on load
getSSRProps(binding) {
if (binding.value !== false) {
return { autofocus: true }
}
return {}
}
}
<template>
<input v-focus type="text" placeholder="Auto-focused input" />
</template>
<script setup>
import { vFocus } from '@/directives/vFocus'
</script>
Directive with Dynamic ID
// CORRECT: Generate consistent IDs
const vId = {
mounted(el, binding) {
el.id = binding.value || `el-${binding.instance?.$.uid}`
},
getSSRProps(binding, vnode) {
// Use the same ID generation logic
return {
id: binding.value || `el-${vnode.component?.uid || 'ssr'}`
}
}
}
Handling Complex Directives
For directives that do more than set attributes, consider:
// Directive that only makes sense on client (e.g., drag-and-drop)
const vDraggable = {
mounted(el, binding) {
// Complex client-side logic
initDragAndDrop(el, binding.value)
},
unmounted(el) {
destroyDragAndDrop(el)
},
// SSR: Just mark element as draggable for styling/semantics
getSSRProps(binding) {
return {
draggable: 'true',
'data-draggable': '',
role: 'listitem'
}
}
}
Directives That Cannot Have SSR Equivalents
Some directives have no meaningful server-side representation:
// Directive that tracks mouse position - no SSR equivalent
const vMousePosition = {
mounted(el, binding) {
el.addEventListener('mousemove', (e) => {
binding.value?.(e.clientX, e.clientY)
})
},
// Nothing meaningful to render on server
getSSRProps() {
return {} // Empty object - no attributes
}
}
Nuxt.js Directive Registration
// plugins/directives.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('tooltip', {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
},
getSSRProps(binding) {
return { 'data-tooltip': binding.value }
}
})
})
Testing SSR Directives
import { renderToString } from 'vue/server-renderer'
import { createSSRApp, h } from 'vue'
import { vTooltip } from './directives/vTooltip'
test('vTooltip renders attributes during SSR', async () => {
const app = createSSRApp({
directives: { tooltip: vTooltip },
template: '<button v-tooltip="\'Help text\'">Click</button>'
})
const html = await renderToString(app)
expect(html).toContain('data-tooltip="Help text"')
expect(html).toContain('aria-label="Help text"')
})