Best Practices
Conditional Rendering
Always check hasContent or visible before rendering elements:
<h1 v-if="titleDesign.visible && title.hasContent" :style="titleStyle">
{{ title.value }}
</h1>Computed Styles
Use computed properties for style objects to ensure reactivity:
<script setup lang="ts">
const titleStyle = computed(() => ({
fontSize: `${titleDesign.size}px`,
fontFamily: titleDesign.font,
color: (titleDesign.color as Color).hex
}));
</script>SSR vs Client-Only Rendering
Sections use server-side rendering (SSR) — the Vue component renders to HTML on the server first, then hydrates in the browser. Browser APIs like window, document, and addEventListener are not available during SSR.
Guard browser values with typeof window:
const windowWidth = ref<number>(typeof window !== 'undefined' ? window.innerWidth : 0);Use onMounted for client-only logic:
Code inside onMounted only runs in the browser after hydration. Use it for event listeners, DOM measurements, animations, and any browser-API-dependent setup:
import { onMounted, onBeforeUnmount } from 'vue';
onMounted(() => {
initializeSlider();
window.addEventListener('resize', onResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', onResize);
});General rule: Keep <script setup> top-level code SSR-safe — composables, computed properties, and reactive refs with safe defaults. Move all browser-specific logic into onMounted. If a piece of UI should only render on the client, guard it with a ref that flips in onMounted:
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const isMounted = ref(false);
onMounted(() => { isMounted.value = true; });
</script>
<template>
<div v-if="isMounted">
<!-- Client-only content -->
</div>
</template>