Skip to content
Twilight React Playground
ثيم رائدaren

useOpeningHours

hookBeginnerserverbrowserlive demo

Works out from a weekly schedule whether the store is open right now and, when closed, when it opens next.

import { useOpeningHours, UseOpeningHoursResult } from '@salla.sa/twilight-theme-engine/hooks';

In plain words

Give it the store's weekly hours, such as monday: ['09:00-17:00']. It tells you whether the store isOpen at this moment and, when it is closed, gives you a readable nextOpenFormatted such as "Sunday 9:00 AM" in the page language.

isEnabled is false when opening hours are switched off or missing, and then the store counts as open.

Signature

function useOpeningHours(settings?: OpeningHoursSettings): UseOpeningHoursResult

interface OpeningHoursSettings {       // from @salla.sa/twilight-theme-engine/types
  enabled?: boolean;
  hours?: Record<string, string[]>;    // monday … sunday
}

interface UseOpeningHoursResult {
  isOpen: boolean;
  isEnabled: boolean;
  nextOpen: Date | null;
  nextOpenFormatted: string;           // '' when open or not configured
}

Try it live

The same hours every day, checked against your own clock. The status is computed when the component renders.Try this: set a range that has already ended today, then try an overnight range such as 22:00-02:00: it is ignored and the store reads as open.
Storefront canvas · en · LTR
Runs in the browser…
Controls
enabled
HH:MM-HH:MM, or 9:00 AM - 5:00 PM.
Closed today
What a theme writes
import { useOpeningHours } from '@salla.sa/twilight-theme-engine/hooks';

// Module scope keeps the object stable between renders.
const OPENING_HOURS = {
  enabled: true,
  hours: { sunday: ['09:00-17:00'], monday: ['09:00-17:00'] /* …every day */ },
};

export function OpenStatus() {
  const { isOpen, isEnabled, nextOpenFormatted } = useOpeningHours(OPENING_HOURS);
  if (!isEnabled) return null;
  return <p>{isOpen ? 'Open now' : `Closed. Opens ${nextOpenFormatted}`}</p>;
}

Example

app/components/store/OpenStatus.tsx
import { useOpeningHours } from '@salla.sa/twilight-theme-engine/hooks';
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';

export function OpenStatus() {
  const { settings } = useStore();
  const { isOpen, isEnabled, nextOpenFormatted } = useOpeningHours(settings.opening_hours);

  if (!isEnabled || isOpen) return null;
  return <p className="store-closed">Closed now. Opens {nextOpenFormatted}</p>;
}

How it behaves

  • Ranges are 'HH:MM-HH:MM' or with AM/PM ('9:00 AM - 5:00 PM'). Day keys must be lowercase English day names.

  • When closed, it looks for a later range today, then for the first range on each of the next 7 days. If none exists it reports open, with isEnabled: true.

  • Day names and AM/PM come from the translation keys common.days.* and common.time.am/pm, joined as "<day> h:mm <AM|PM>".

  • The result is memoised on [settings, t]: keep the settings object stable (store settings are).

  • The default StoreClosedNotification handler on body:start uses it.

  • It has no subpath of its own: import it from @salla.sa/twilight-theme-engine/hooks.

Gotchas

  • It uses the clock and time zone of wherever it runs: UTC on the server while it renders, the shopper's zone in the browser, and neither is necessarily the store's. The result can differ between the server HTML and hydration. Render it after mount (useIsClient) when the difference matters.

  • It does not tick. The status is computed when the component renders, so an open page does not flip at opening time until something re-renders it.

  • An overnight range such as '22:00-02:00' is dropped (a range must start before it ends), and a time it cannot parse counts as midnight.

  • opening_hours is marked "TODO: add to API response" in the StoreSettings type; this demo store does not send it today, so pass your own schedule if you need one.

Related

Source and docs