CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-responsive

Media queries in React for responsive design with hook and component APIs

Pending
Overview
Eval results
Files

context-utilities.mddocs/

Context and Utilities

Context providers for server-side rendering scenarios and utility functions for media query string generation and device property management.

Capabilities

React Context

React Context for providing device settings to all useMediaQuery hooks and MediaQuery components in the component tree. Essential for server-side rendering and testing scenarios.

/**
 * React Context for providing device settings globally
 * Used for server-side rendering and testing with specific device characteristics
 */
const Context: React.Context<Partial<MediaQueryAllQueryable> | undefined>;

Usage Examples:

import React from "react";
import { Context as ResponsiveContext, useMediaQuery, MediaQuery } from "react-responsive";

// Server-side rendering with device context
const SSRExample = () => {
  // This would typically be set based on User-Agent headers
  const deviceSettings = {
    width: 768,
    height: 1024,
    orientation: "portrait" as const,
  };

  return (
    <ResponsiveContext.Provider value={deviceSettings}>
      <App />
    </ResponsiveContext.Provider>
  );
};

// Testing with specific device characteristics
const TestingExample = () => {
  const mobileDevice = {
    width: 375,
    height: 667,
    orientation: "portrait" as const,
  };

  const desktopDevice = {
    width: 1920,
    height: 1080,
    orientation: "landscape" as const,
  };

  return (
    <div>
      <h2>Mobile Test</h2>
      <ResponsiveContext.Provider value={mobileDevice}>
        <ResponsiveComponent />
      </ResponsiveContext.Provider>

      <h2>Desktop Test</h2>
      <ResponsiveContext.Provider value={desktopDevice}>
        <ResponsiveComponent />
      </ResponsiveContext.Provider>
    </div>
  );
};

// Component that uses the context
const ResponsiveComponent = () => {
  const isMobile = useMediaQuery({ maxWidth: 767 });
  const isDesktop = useMediaQuery({ minWidth: 1024 });

  return (
    <div>
      <MediaQuery maxWidth={767}>
        <p>Mobile view (from component)</p>
      </MediaQuery>
      {isMobile && <p>Mobile view (from hook)</p>}
      {isDesktop && <p>Desktop view (from hook)</p>}
    </div>
  );
};

Real-world SSR Example with Next.js:

// pages/_app.tsx
import { Context as ResponsiveContext } from "react-responsive";
import { GetServerSideProps } from "next";

interface AppProps {
  deviceSettings?: {
    width: number;
    height: number;
    orientation: "portrait" | "landscape";
  };
}

const MyApp = ({ Component, pageProps, deviceSettings }: AppProps) => {
  return (
    <ResponsiveContext.Provider value={deviceSettings}>
      <Component {...pageProps} />
    </ResponsiveContext.Provider>
  );
};

// Server-side device detection
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
  const userAgent = req.headers["user-agent"] || "";
  
  // Simple device detection (in practice, use a proper library)
  const isMobile = /Mobile|Android|iPhone|iPad/i.test(userAgent);
  
  const deviceSettings = {
    width: isMobile ? 375 : 1920,
    height: isMobile ? 667 : 1080,
    orientation: isMobile ? "portrait" as const : "landscape" as const,
  };

  return {
    props: {
      deviceSettings,
    },
  };
};

Testing with React Testing Library:

import { render, screen } from "@testing-library/react";
import { Context as ResponsiveContext } from "react-responsive";
import MyResponsiveComponent from "./MyResponsiveComponent";

describe("MyResponsiveComponent", () => {
  test("renders mobile layout", () => {
    const mobileDevice = { width: 375, height: 667 };
    
    render(
      <ResponsiveContext.Provider value={mobileDevice}>
        <MyResponsiveComponent />
      </ResponsiveContext.Provider>
    );

    expect(screen.getByText("Mobile Layout")).toBeInTheDocument();
    expect(screen.queryByText("Desktop Layout")).not.toBeInTheDocument();
  });

  test("renders desktop layout", () => {
    const desktopDevice = { width: 1920, height: 1080 };
    
    render(
      <ResponsiveContext.Provider value={desktopDevice}>
        <MyResponsiveComponent />
      </ResponsiveContext.Provider>
    );

    expect(screen.getByText("Desktop Layout")).toBeInTheDocument();
    expect(screen.queryByText("Mobile Layout")).not.toBeInTheDocument();
  });
});

toQuery Utility

Utility function that converts JavaScript object representation of media queries to CSS media query strings.

/**
 * Converts media query object to CSS media query string
 * @param obj - Object with media query properties
 * @returns CSS media query string
 */
function toQuery(obj: Partial<MediaQueryAllQueryable>): string;

Usage Examples:

import { toQuery } from "react-responsive";

// Basic conversion
const queryString = toQuery({ minWidth: 768, maxWidth: 1024 });
console.log(queryString); // "(min-width: 768px) and (max-width: 1024px)"

// Complex query with multiple properties
const complexQuery = toQuery({
  minWidth: 768,
  orientation: "landscape",
  color: true,
  type: "screen"
});
console.log(complexQuery); 
// "screen and (min-width: 768px) and (orientation: landscape) and color"

// Boolean properties
const printQuery = toQuery({
  type: "print",
  color: false, // Negated
  monochrome: true
});
console.log(printQuery); // "print and not color and monochrome"

// Number conversion to px
const dimensionQuery = toQuery({
  width: 1024,
  height: 768,
  minResolution: "2dppx"
});
console.log(dimensionQuery); 
// "(width: 1024px) and (height: 768px) and (min-resolution: 2dppx)"

Integration with Custom Logic:

import { toQuery, useMediaQuery } from "react-responsive";

const CustomMediaQueryHook = (breakpoints: Record<string, Partial<MediaQueryAllQueryable>>) => {
  const results: Record<string, boolean> = {};
  
  Object.entries(breakpoints).forEach(([name, query]) => {
    const queryString = toQuery(query);
    console.log(`${name} query:`, queryString);
    
    // Use the generated query string with useMediaQuery
    results[name] = useMediaQuery({ query: queryString });
  });
  
  return results;
};

// Usage
const ResponsiveComponent = () => {
  const queries = CustomMediaQueryHook({
    mobile: { maxWidth: 767 },
    tablet: { minWidth: 768, maxWidth: 1023 },
    desktop: { minWidth: 1024 },
    portrait: { orientation: "portrait" },
    retina: { minResolution: "2dppx" }
  });

  return (
    <div>
      {queries.mobile && <p>Mobile device</p>}
      {queries.tablet && <p>Tablet device</p>}
      {queries.desktop && <p>Desktop device</p>}
      {queries.portrait && <p>Portrait orientation</p>}
      {queries.retina && <p>High DPI display</p>}
    </div>
  );
};

Media Query Object Properties

The utility functions and context work with the complete set of media query properties:

interface MediaQueryAllQueryable extends MediaQueryFeatures, MediaQueryTypes {
  // Dimension properties (support min/max variants)
  width?: number | string;
  height?: number | string;
  deviceWidth?: number | string;
  deviceHeight?: number | string;
  
  // Aspect ratio properties (support min/max variants)
  aspectRatio?: string;
  deviceAspectRatio?: string;
  
  // Display properties
  color?: boolean;
  colorIndex?: boolean;
  monochrome?: boolean;
  
  // Resolution properties (support min/max variants)
  resolution?: number | string;
  
  // Orientation and scan properties
  orientation?: "portrait" | "landscape";
  scan?: "progressive" | "interlace";
  
  // Media type properties
  type?: "all" | "grid" | "aural" | "braille" | "handheld" | "print" | 
         "projection" | "screen" | "tty" | "tv" | "embossed";
         
  // All boolean media types
  all?: boolean;
  grid?: boolean;
  aural?: boolean;
  braille?: boolean;
  handheld?: boolean;
  print?: boolean;
  projection?: boolean;
  screen?: boolean;
  tty?: boolean;
  tv?: boolean;
  embossed?: boolean;
  
  // Min/max variants for all dimension properties
  minWidth?: number | string;
  maxWidth?: number | string;
  minHeight?: number | string;
  maxHeight?: number | string;
  minDeviceWidth?: number | string;
  maxDeviceWidth?: number | string;
  minDeviceHeight?: number | string;
  maxDeviceHeight?: number | string;
  minAspectRatio?: string;
  maxAspectRatio?: string;
  minDeviceAspectRatio?: string;
  maxDeviceAspectRatio?: string;
  minColor?: number;
  maxColor?: number;
  minColorIndex?: number;
  maxColorIndex?: number;
  minMonochrome?: number;
  maxMonochrome?: number;
  minResolution?: number | string;
  maxResolution?: number | string;
}

Advanced Context Patterns

Dynamic Context Updates:

import React, { createContext, useContext, useState, useEffect } from "react";
import { Context as ResponsiveContext } from "react-responsive";

// Custom context that updates based on actual window size
const DynamicResponsiveProvider = ({ children }: { children: React.ReactNode }) => {
  const [deviceSettings, setDeviceSettings] = useState({
    width: typeof window !== "undefined" ? window.innerWidth : 1024,
    height: typeof window !== "undefined" ? window.innerHeight : 768,
  });

  useEffect(() => {
    const handleResize = () => {
      setDeviceSettings({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return (
    <ResponsiveContext.Provider value={deviceSettings}>
      {children}
    </ResponsiveContext.Provider>
  );
};

Type Definitions

import { Context } from "react";

const Context: Context<Partial<MediaQueryAllQueryable> | undefined>;

function toQuery(obj: Partial<MediaQueryAllQueryable>): string;

interface MediaQueryAllQueryable {
  // Complete interface with all media query properties
  // (See full definition in index.md)
}

Install with Tessl CLI

npx tessl i tessl/npm-react-responsive

docs

component-api.md

context-utilities.md

hook-api.md

index.md

tile.json