A container component reacting to breakpoints on its own width or height, powered by ResizeObserver and scoped slots.
It is similar to things like vue-resize
in that you can use it to observe a component's size. However the ResponsiveContainer
will not emit any events, its whole point is to work as declaratively as possible, taking a predefined set of breakpoints you can access in your component.
The ResponsiveContainer
component is used as a wrapper — an elementary example could look like this:
<ResponsiveContainer
default-breakpoint="tiny"
:breakpoints="{ small: 300, medium: 450, large: 600 }"
v-slot="container"
>
The current breakpoint is: {{ container.breakpoint }}
</ResponsiveContainer>
The v-slot
property above is the key to using the responsive container: It provides an API for reading information and detecting all kinds of changes.
Notes:
container
is just an arbitrarily chosen name, you can call it whatever you want or even use object destructuring.v-slot
is a new way of using slots since Vue 2.6. If you use Vue 2.5 or older, you'll have to use the deprecatedslot-scope
attribute.
- The concept of the
ResponsiveContainer
is strictly one-dimensional, either width or height can be observed. You can however nest a width-observing and a height-observing container if needed. - The breakpoints are modeled after "classic" responsive design. Each breakpoint has a lower bound (or an upper bound if you set it to perform
desktop-first
) and there is no way to configure min-max-ranges et al. - The
ResizeObserver
used under the hood is not available in older browsers. There are polyfills that work well across browsers, but you'll have to include them yourself. - Also don't forget to install the
lodash
dependency.
The full API (in TypeScript syntax) looks like this:
// This is what gets passed to the `v-slot` attribute
interface ResponsiveContainerAPI {
// Check properties of a specific [breakpoint]
is: {
// Is [breakpoint] the current one?
exactly: BreakpointSwitches,
// Is the current breakpoint smaller than or equal to [breakpoint]?
atMost: BreakpointSwitches,
// Is the current breakpoint smaller than [breakpoint]?
smallerThan: BreakpointSwitches,
// Is the current breakpoint larger than or equal to [breakpoint]?
atLeast: BreakpointSwitches
// Is the current breakpoint larger than [breakpoint]?
largerThan: BreakpointSwitches
},
// The currently active breakpoint
breakpoint: BreakpointName,
// The default breakpoint
defaultBreakpoint: BreakpointName,
// The provided breakpoints name/size mapping
breakpoints: { [key: BreakpointName]: BreakpointSize },
// The used responsive strategy
strategy: 'mobile-first' | 'desktop-first',
// The breakpoints that are smaller than the current one
smaller: BreakpointName[],
// The breakpoints that are larger than the current one
larger: BreakpointName[],
// The current size (width or height) of the responsive container
size: number
}
// Any of the given breakpoints' name
type BreakpointName = string
// Any of the given breakpoints' size
type BreakpointSize = number
// Maps all breakpoint names to true/false, depending on various conditions
type BreakpointSwitches = { [key: BreakpointName]: boolean }
breakpoints
Type: { [name: string]: number }
The breakpoints to check for as a mapping from breakpoint names to sizes.
default-breakpoint
Type: string
The default breakpoint used when none of the given breakpoints
matches.
strategy
Type: 'mobile-first' | 'desktop-first'
Default: 'mobile-first'
The strategy to use in the container. Basically decides if the default-breakpoint
is smaller than all defined breakpoints (mobile-first) or larger (desktop-first).
dimension
Type: 'width' | 'height'
Default: 'width'
The dimension to observe.
throttle
Type: number
Default: 200
How many milliseconds are at least awaited between two size change reports. Throttling is leading
and trailing
.
tag
Type: string | object | null
Default: 'div'
The container needs to render an element that is watched by the ResizeObserver
. By default, this is a <div>
, but it can be any tag or Vue component. Rendering can be disabled through passing null
, however this enforces passing a single child element to the slot, which will be used for measuring.