Интеграция с фреймворками
Интеграция с Vue 3
Vue composables включены в основной бандл и активируются, когда Vue установлен как peer-зависимость.
Регистрация плагина
import { createApp } from 'vue'
import { ResponsivePlugin } from 'responsive-media'
const app = createApp(App)
app.use(ResponsivePlugin, {
sm: [{ type: 'max-width', value: 767 }],
lg: [{ type: 'min-width', value: 1024 }],
})
app.mount('#app')useResponsive<T>(): объект реактивного состояния
Возвращает Vue-реактивное responsive-состояние. Реактивно в шаблонах и computed-свойствах.
<script setup lang="ts">
import { useResponsive } from 'responsive-media'
type MyState = { sm: boolean; lg: boolean }
const state = useResponsive<MyState>()
</script>
<template>
<MobileNav v-if="state.sm" />
<DesktopNav v-else />
</template>useBreakpoints(): упорядоченные хелперы
Возвращает реактивные упорядоченные хелперы брейкпоинтов. Все методы реагируют на изменения вьюпорта в шаблонах.
<script setup>
import { useBreakpoints } from 'responsive-media'
const { current, isAbove, isBelow, between } = useBreakpoints()
</script>
<template>
<span>Current: {{ current }}</span>
<DesktopNav v-if="isAbove('sm')" />
<MobileNav v-else />
<TabletOnly v-if="between('sm', 'lg')" />
</template>
current— этоComputedRef<string | null>.isAbove,isBelow,between— обычные функции, реактивные благодаря тому, что читают из Vue-реактивного состояния.
useMediaQuery(query): одиночный сырой запрос
Возвращает Ref<boolean> для сырой строки CSS media query. Автоматически убирается при onUnmounted.
<script setup>
import { useMediaQuery } from 'responsive-media'
const isDark = useMediaQuery('(prefers-color-scheme: dark)')
const canHover = useMediaQuery('(hover: hover)')
</script>
<template>
<DarkTheme v-if="isDark" />
</template>useContainerState(elementRef, config, options?): контейнерные запросы
Отслеживает размеры элемента и возвращает объект реактивного состояния. Автоматически настраивает и убирает ResizeObserver через watchEffect.
<script setup>
import { useTemplateRef } from 'vue'
import { useContainerState } from 'responsive-media'
const cardRef = useTemplateRef('card')
const cardState = useContainerState(cardRef, {
compact: [{ type: 'max-width', value: 300 }],
wide: [{ type: 'min-width', value: 600 }],
})
</script>
<template>
<div ref="card">
<CompactLayout v-if="cardState.compact" />
<WideLayout v-else-if="cardState.wide" />
<DefaultLayout v-else />
</div>
</template>Интеграция с React 19+
Импортируйте из responsive-media/react.
import {
useResponsive,
useBreakpoints,
useMediaQuery,
useContainerState,
} from 'responsive-media/react'useResponsive<T>(): реактивное состояние
Возвращает текущее responsive-состояние. Ререндерится только при изменении состояния. Внутри использует useSyncExternalStore.
import { useResponsive } from 'responsive-media/react'
type MyState = { sm: boolean; lg: boolean }
function App() {
const { sm, lg } = useResponsive<MyState>()
return sm ? <MobileNav /> : <DesktopNav />
}useBreakpoints(): упорядоченные хелперы
Возвращает упорядоченные хелперы брейкпоинтов. Ререндерится при изменении responsive-состояния.
import { useBreakpoints } from 'responsive-media/react'
function Nav() {
const { current, isAbove, isBelow, between } = useBreakpoints()
return (
<>
<span>Current: {current}</span>
{isAbove('sm') ? <DesktopNav /> : <MobileNav />}
{between('sm', 'lg') && <TabletBanner />}
</>
)
}В отличие от Vue,
current— обычнаяstring | null(не ref). Ререндеры запускаются черезuseSyncExternalStore.
useMediaQuery(query): одиночный сырой запрос
Возвращает boolean, отслеживающий сырую строку CSS media query. SSR-безопасно (возвращает false на сервере).
import { useMediaQuery } from 'responsive-media/react'
function ThemeToggle() {
const isDark = useMediaQuery('(prefers-color-scheme: dark)')
const canHover = useMediaQuery('(hover: hover)')
return <button className={isDark ? 'dark' : 'light'}>Toggle</button>
}useContainerState(ref, config, options?): контейнерные запросы
Отслеживает размеры элемента и возвращает объект состояния. Настраивает и убирает ResizeObserver через useEffect.
import { useRef } from 'react'
import { useContainerState } from 'responsive-media/react'
function Card() {
const ref = useRef<HTMLDivElement>(null)
const { compact, wide } = useContainerState(ref, {
compact: [{ type: 'max-width', value: 300 }],
wide: [{ type: 'min-width', value: 600 }],
})
return (
<div ref={ref}>{compact ? <CompactLayout /> : wide ? <WideLayout /> : <DefaultLayout />}</div>
)
}
configиoptionsсчитаются статичными после монтирования. Оборачивайте вuseMemo, если они меняются.
Хелперы TypeScript
ConfigToState<T>
Выводит тип булева состояния из объекта конфига:
import type { ConfigToState, MediaQueryConfig } from 'responsive-media'
const config = {
sm: [{ type: 'min-width', value: 640 }],
lg: [{ type: 'min-width', value: 1024 }],
} satisfies Record<string, MediaQueryConfig>
type MyState = ConfigToState<typeof config>
// → { sm: boolean; lg: boolean }
const { sm, lg } = responsiveState.getState<MyState>()Обобщённый useResponsive<T>()
Оба адаптера, Vue и React, принимают обобщённый параметр типа для сужения возвращаемого состояния:
type AppState = { mobile: boolean; tablet: boolean; desktop: boolean }
const state = useResponsive<AppState>()SSR и гидратация
Все API SSR-безопасны — они проверяют доступность window, matchMedia и ResizeObserver перед использованием и откатываются на false на сервере.
| Сценарий | Поведение |
|---|---|
typeof window === 'undefined' | Все слушатели пропускают настройку; proxy / getState() возвращают false для всех ключей |
useCookie на сервере (Nuxt) | Неприменимо — используйте useCookie из @vueuse/core или встроенные средства Nuxt |
| React SSR | useMediaQuery возвращает false; useResponsive возвращает серверный снапшот |
| Несовпадение при гидратации | Вызовите hydrate() с серверным снапшотом перед первым рендером |
// Сервер: сериализуем ожидаемое начальное состояние
const initialState = { mobile: false, tablet: false, desktop: true }
// Клиент: гидратируем перед первым рендером
import { responsiveState } from 'responsive-media'
responsiveState.hydrate(initialState)