viewportjs

ViewportJS is built on top of `window.matchMedia` and provides valuable features that enable more structure when querying and subscribing to media queries.

Usage no npm install needed!

<script type="module">
  import viewportjs from 'https://cdn.skypack.dev/viewportjs';
</script>

README

ViewportJS

NPM version Build Status Total downloads Maintainability

ViewportJS is built on top of window.matchMedia and provides valuable features that enable more structure when querying and subscribing to media queries.

  • 1.14 kB minified & gzipped.

  • Supports all browsers that support window.matchMedia.

  • Compatible with CommonJS, AMD, and browser globals (via UMD).

  • Supports SSR (server-side rendering) by providing a shallow API when window.matchMedia is unavailable.

Give the demo a try by changing the size of your browser window and watch the UI update.

If you are upgrading from v3, please see the v4 migration guide.


Installation

CommonJS

Install the latest version from npm:

npm install viewportjs

Add the viewportjs package to your app:

const viewport = require( 'viewportjs' );

const myViewports = viewport( /* configuration */ );

AMD

The API is exported as an anonymous module. If you're not familiar with AMD, RequireJS is a great place to start.

Browser Global

Download the latest development and production versions from UNPKG. Once the script is loaded, the viewport function can be accessed globally.

<!-- When deploying, replace "viewport.js" with "viewport.min.js". -->
<script src="viewport.js"></script>
<script>
  const myViewports = viewport( /* configuration */ );
</script>

Usage

Configure viewports by name:

const myViewports = viewport( [
    {
        name: 'small',
        query: '( min-width:0px ) and ( max-width:480px )'
    },
    {
        name: 'medium',
        query: '( min-width:480px ) and ( max-width:767px )'
    },
    {
        name: 'large',
        query: '( min-width:769px )'
    }
] );

Once configured, you can query their state:

// Check if `small` is the current viewport
myViewports.current( 'small' );  // boolean

// Get the current viewport object
myViewports.current(); // { name: 'small', matches: boolean, current: boolean }

// Check if the `small` viewport's media query matches
myViewports.matches( 'small' ); // boolean

// Retrieve all matches
myViewports.matches(); // [ /* matches viewport state objects */ ]

You can also subscribe to state changes:

// Subscribe to changes in 'small' viewport
myViewports( 'small', state => {

  // Do something based on `state`
  // {
  //    name: 'small',
  //    matches: boolean,
  //    current: boolean
  // }
  
} );

Configuration

The initialization method takes an array of viewport configuration objects that are composed of two properties:

  • name (string) The viewport's nickname. Must be unique.

  • query (string) A valid mediaQueryString.

The order of these objects in the array is important, as it determines how to calculate the current viewport, which is defined as the last matching viewport based on the order of the configuration array. So if you prefer a "mobile-first" approach, your viewport configuration objects should be ordered from smallest to largest.

The initialization method returns a configured instance that can act as:

  • A function used to subscribe to viewport changes.
  • An object that contains methods for querying viewport state.

Subscribing to Viewport Changes

The initialization method returns an instance that can be used to subscribe to state changes on the configured viewports.

Arguments:

  • name: (string) (optional) The name of a configured viewport.
  • handler: (Function) The function to execute whenever state changes occur.

Returns:

  • (Function): A function that unsubscribes handler.

To subscribe to the state of an individual viewport, both name and handler are required. Providing only a handler will set up a subscription to the states of all configured viewports.

A subscriber's handler is executed whenever there's a change in either the viewport's matched or current state. When a subscriber is added, its handler will be immediately executed.

The handler receives the following arguments when executed:

  • state: (Object) The changed viewport's state object.
  • instance: (Object) The configured instance.

A viewport state object has three properties:

  • name: (string) The name of a configured viewport.
  • matches: (boolean) If the viewport's media query matches.
  • current: (boolean) If the viewport is current.

Example:

const myViewports = viewport( /* configuration */ );

// Subscribe to an individual configured viewport
myViewports( 'name', state => {} );

// Subscribe to all configured viewport
myViewports( state => {} )

Subscribing Directly to a mediaQueryString

For scenarios where you're only interested in matching a single media query, you can provide the initialization method with a valid mediaQueryString and an optional handler, instead of a configuration array. This will return an instance with a limited API.

Arguments:

  • query: (string) A valid mediaQueryString.
  • handler: (Function) (optional) The function to execute whenever state changes occur.

Returns:

  • (Object): A limited API composed of the matches() and remove() method.

If provided, handler is executed whenever there's a change in the media query's matched state, including on initial subscription.

The handler receives the following arguments when executed:

  • matches: (boolean) If the media query matches.
  • instance: (Object) The configured instance.

Example:

const smallvp = viewport( '( max-width: 500px )', ( matches, instance ) => {} );

smallvp.matches(); // true/false
smallvp.remove(); // remove the handler, if provided.

Instance Methods

current( [name] )

When called with the name argument, checks if name is the current viewport and returns a boolean. Otherwise, it returns the current viewport's state object.

The current viewport is defined as the last matching viewport based on the order of the configuration array. If there is no current viewport, the state object for the undefined viewport is returned: { name: undefined, matches: false, current: false }.

Arguments:

  • name: (string) (optional) The name of the configured viewport to check.

Returns:

  • (boolean): If name is the current viewport.
  • (Object): The state object of the current viewport.
myViewports.current(); // { name: string, matches: boolean, current: boolean }

myViewports.current( 'name' ); // true/false

matches( [name] )

When called with the name argument, checks if the name viewport's media query matches. Otherwise, it returns an array of all matching viewports.

If there are no matching viewports, an empty array is returned.

Arguments:

  • name: (string) (optional) The name of the configured viewport to check.

Returns:

  • (boolean): If the name viewport matches.
  • (Array): An array of state objects for all matching viewports.
myViewports.matches(); // [ { name: string, matches: boolean, current: boolean }, ... ]

myViewports.matches( 'name' ); // true/false

previous( [name] )

When called with the name argument, checks if the name viewport was the previously current viewport. Otherwise, it returns the previously current viewport's state object.

If there was no previously current viewport, the state object for the undefined viewport is returned: { name: undefined, matches: false, current: false }.

Arguments:

  • name: (string) (optional) The name of the configured viewport to check.

Returns:

  • (boolean): If name was the previously current viewport.
  • (Object): The state object of the previously current viewport.
myViewports.previous(); // { name: string, matches: boolean, current: boolean }

myViewports.previous( 'name' ); // true/false

remove()

Removes all the instance's configured viewports and subscribers at once.

Returns:

  • (null): Subscribers are removed and values set to null.
const myViewports = viewport( /* viewport config array */ );

myvps( 'small', state => {} );
myvps( 'medium', state => {} );

myvps.remove();

state( [name] )

When called with the name argument, returns the named viewport's state object. Otherwise, it returns an array of state objects for all viewports.

Arguments:

  • name: (string) (optional) The name of the configured viewport to check.

Returns:

  • (Object): The state object of the named viewport.
  • (Array): An array of state objects for all viewports.
myViewports.state(); // [ { name: string, matches: boolean, current: boolean }, ... ]

myViewports.previous( 'name' ); // { name: string, matches: boolean, current: boolean }

Server-Side Rendering

ViewportJS supports SSR (or "Universal JavaScript") through a shallow API that enables the use of all methods in an environment where window.matchMedia is unavailable.

Due to potential memory leaks, calls that subscribe to viewports should only be made when their respective unsubscribe functions (or the instance's remove() method) can be called in the same environment. Initialization and query methods can be used in any environment, but it's best if subscriptions are made in code that only executes in the browser. The development build of ViewportJS will log a warning whenever a subscription is made in an environment where window.matchMedia is unavailable. All logging is removed in the production build.

See the relevant framework examples below for SSR-compatible demonstrations.

Examples