useResponsiveLayout
The one place that decides what "mobile" means. Three bands, and the off-canvas drawer state shared by every layout.
Import
ts
import { computedIsMobile, isPreviewFrame, useResponsiveLayout } from '@/layouts/useResponsiveLayout'Signature
ts
function useResponsiveLayout(): {
isMobile: Readonly<Ref<boolean>>
isTablet: Readonly<Ref<boolean>>
isDesktop: Readonly<Ref<boolean>>
mobileNavOpen: Ref<boolean>
openMobileNav: () => void
closeMobileNav: () => void
railFor: (configuredCollapsed: boolean) => boolean
toggleSidebar: () => void
}
function isPreviewFrame(): boolean
const computedIsMobile: ComputedRef<boolean>The three bands
| Band | Width | The sidebar is |
|---|---|---|
isMobile | below 768 | Not in the layout at all — an off-canvas sheet replaces it. |
isTablet | 768 to 1023 | Present, forced to the icon rail. |
isDesktop | 1024 and up | Whatever config.ui.sidebarStyle says. |
Returns
| Member | Type | Description |
|---|---|---|
isMobile / isTablet / isDesktop | Readonly<Ref<boolean>> | Which band the window is in. |
mobileNavOpen | Ref<boolean> | Drawer state, writable. |
openMobileNav / closeMobileNav | () => void | Open and close it. |
railFor | (configuredCollapsed) => boolean | Should the sidebar draw as the icon rail right now? |
toggleSidebar | () => void | Collapses the sidebar on desktop; toggles the drawer otherwise. Bound to t s. |
isPreviewFrame() | boolean | Whether this is running inside the responsive preview's iframe. |
computedIsMobile | ComputedRef<boolean> | isMobile as a computed, for code that needs one. |
Example
vue
<script setup lang="ts">
import { useResponsiveLayout } from '@/layouts/useResponsiveLayout'
import { useConfig } from '@core/config/useConfig'
import { computed } from 'vue'
const { isMobile, mobileNavOpen, railFor, toggleSidebar } = useResponsiveLayout()
const cfg = useConfig()
const rail = computed(() => railFor(cfg.config.ui.sidebarStyle === 'collapsed'))
</script>
<template>
<div class="flex">
<aside v-if="!isMobile" :class="rail ? 'w-16' : 'w-64'">
<slot name="nav" />
</aside>
<button type="button" @click="toggleSidebar()">
Menu
</button>
<div v-if="isMobile && mobileNavOpen" class="fixed inset-0">
<slot name="nav" />
</div>
</div>
</template>Notes
- The tablet rail is derived, never written. The config is persisted, so writing
sidebarStyle: 'collapsed'on a resize would overwrite the reader's stored desktop preference and survive the reload.railForcombines the stored preference with the current band instead. toggleSidebardoes two different things on purpose. On desktop, with a layout that has a sidebar, it flips the storedsidebarStyle— the same write the sidebar's own chevron makes. Belowlg, or in a layout with no sidebar at any width, there is nothing to collapse, so it opens the drawer.mobileNavOpenis module-scope, so the navbar's hamburger and the drawer itself are the same switch no matter which of the four layouts composed them.isPreviewFrame()reads?sa-previewfrom the URL. The responsive preview loads the real app in an iframe, so without this the embedded copy would render its own preview button and nest another frame. Taking it from the URL means no state has to thread through four layouts.
Related
- Layouts — the four shells and their breakpoints.
useLayout— which shell renders.useResponsivePreview— the iframe this detects.- Navigation — what the drawer contains.