Skip to content

feat(hooks): add 'useIsClient' #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

sukvvon
Copy link
Contributor

@sukvvon sukvvon commented Apr 25, 2025

Overview

Checklist

  • Did you write the test code?
  • Have you run yarn run fix to format and lint the code and docs?
  • Have you run yarn run test:coverage to make sure there is no uncovered line?
  • Did you write the JSDoc?

@codecov-commenter
Copy link

codecov-commenter commented Apr 25, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (bea638d) to head (3869fb5).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #219   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           33        34    +1     
  Lines          836       842    +6     
  Branches       254       256    +2     
=========================================
+ Hits           836       842    +6     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@seungrodotlee
Copy link
Member

We have similar discuss here: #202

@sukvvon
Copy link
Contributor Author

sukvvon commented May 7, 2025

@seungrodotlee

useIsClient is necessary in an SSR (Server-Side Rendering) environment for the following reasons:

  • To safely control access to browser-only APIs (like window, localStorage, etc.)
  • To conditionally render UI that should only appear on the client side
  • To prevent hydration mismatches between server-rendered HTML and client-rendered React components

@seungrodotlee
Copy link
Member

I understand that distinguishing between the server and client environments is important,
but I'm not quite sure why this must be provided as a hook.
What are the advantages compared to providing it as a constant?

const isClient = typeof window !== 'undefined';

@sukvvon
Copy link
Contributor Author

sukvvon commented May 8, 2025

@seungrodotlee

Using typeof window !== 'undefined' gives a static value before rendering, which can lead to hydration mismatches between SSR and CSR. On the other hand, useIsClient leverages useEffect, so it only turns true after the component mounts on the client.

This allows:

  • Safe use of browser-only APIs
  • Conditional rendering of client-only UI
  • React-compliant, state-driven transitions between SSR and CSR

In React, a hook like useIsClient is a safer and more reliable approach than using a constant.

@JaeSang1998
Copy link

JaeSang1998 commented May 8, 2025

@sukvvon I think using useSyncExternalStore would be a better approach since it avoids unnecessary re-renders compared to useEffect. Also, i think it follows React's recommended approach in a more standardized way, instead of relying on typeof window checks. What do you think?

import { useSyncExternalStore } from 'react';

const client = {
  subscribe: () => () => {},
  getClientSnapshot: () => true,
  getServerSnapshot: () => false,
};

const useIsClient = () => {
  const isClient = useSyncExternalStore(
    client.subscribe,
    client.getClientSnapshot,
    client.getServerSnapshot
  );

  return isClient;
};

export default useIsClient;

@sukvvon
Copy link
Contributor Author

sukvvon commented May 8, 2025

@JaeSang1998

useSyncExternalStore is available starting from React 18.
Currently, peerDependencies is set to "react": "*", which allows installation in React 17 environments where this hook doesn’t exist and can cause runtime errors.

  • If we need to support React 17, we need to install the use-sync-external-store (shim).
  • If not, it’s better to set peerDependencies to "react": ">=18.0.0" for clarity.

If our library targets React 18+, the latter is the cleaner and safer approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants