51

I'm building a isomorphic application, but I'm using a third-party component that only renders on the client. So, particularly for this component, I need to only render it when I'm rendering in the client.

How do I detect if I'm at the client or at the server? I'm looking for something like isClient() or isServer().

Andre Pena
  • 56,650
  • 48
  • 196
  • 243

9 Answers9

51

Internally, React uses a utility called ExecutionEnvironment for this. It implements a few useful properties like canUseDOM and canUseEventListeners. The solution is essentially just what's suggested here though.

The implementation of canUseDOM

var canUseDOM = !!(
  (typeof window !== 'undefined' &&
  window.document && window.document.createElement)
);

I use this in my application like this

var ExecutionEnvironment = require('react/node_modules/fbjs/lib/ExecutionEnvironment');
...
render() {
  <div>{ ExecutionEnvironment.canUseDOM ? this.renderMyComponent() : null }</div>
}

EDIT This is an undocumented feature that shouldn't be used directly. Its location will likely change from version to version. I shared this as a way of saying "this is the best you can do" by showing what the Facebook team uses internally. You may want to copy this code (it's tiny) into your own project, so you don't have to worry about keeping up with its location from version to version or potential breaking changes.

ANOTHER EDIT Someone created an npm package for this code. I suggest using that.

npm install exenv --save
Luke Taylor
  • 8,631
  • 8
  • 54
  • 92
Charlie Martin
  • 8,208
  • 3
  • 35
  • 41
  • 1
    Just a curiosity, why the !! at the beginning? – Jacopo Mar 19 '20 at 15:40
  • 3
    `!!` casts the value to a boolean to make sure that `true` or `false` is returned instead of potentially `undefined`. You could replace `!!` above with `Boolean` and get the same result – Charlie Martin Mar 19 '20 at 18:33
  • Since here we are using the `&&` operator I can't see a path that can eventually returns undefined, or other values other than true or false. Am I wrong? – Jacopo Mar 19 '20 at 18:46
  • 1
    if `window.document` exists, but has no `createElement` method, this will return `undefined`. If it does have a `createElement` method, it would return that method AKA a function. To test this, you can copy and paste it in your javascript console (without the `!!`) and see that it returns a function – Charlie Martin Mar 19 '20 at 19:45
  • Hey, I was wondering, can we use something like this in the unit test? to check whether the component is actually loaded from the client-side or server-side. – Fahmi Jabbar Jun 26 '22 at 01:52
  • This will tell you if the code is currently running in a browser or not. Do you execute your unit tests in a browser? I doubt it. So I'd imagine this will always be `false` in your unit tests. – Charlie Martin Aug 11 '22 at 03:06
14

You can use reacts lifecyle events (e.g.: componentDidMount) to detect server/client side rendering.

Examples

As Hook

import { useState, useEffect } from 'react'

function useIsServer () {
  const [isServer, setIsServer] = useState(true)
  useEffect(() => {
    setIsServer(false)
  }, [])
  return isServer
}

Usage

See below (Functional Component)

As Functional Component

import useIsServer from './above'

function ServerOnly ({ children = null, onClient = null }) {
  const isServer = useIsServer()
  return isServer
    ? children
    : onClient
}

Usage

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>

As Class Component

class ServerOnly extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      isServer: true
    }
  }

  componentDidMount() {
    this.setState({
      isServer: false
    })
  }

  render () {
    const { isServer } = this.state
    const { children, onClient } = this.props
    return isServer
      ? children
      : onClient
  }
}

Usage

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>
HaNdTriX
  • 28,732
  • 11
  • 78
  • 85
11

Two things that may be relevant:

Many projects use some convention where they set a global SERVER or CLIENT boolean so all your code can switch based off it. In your server bundle, set some global, like in this project

global.__SERVER__ = true;

And in your client bundle, set some global client to true, which you can achieve one way with Webpack's DefinePlugin

new webpack.DefinePlugin({
  __CLIENT__: true
})

With the above approach, you could switch based off that variable in willMount, or render, to do one thing on the server, and another on the client.

The second thing that may be helpful here is componentDidMount only runs on the client, but not on the server.

Andy Ray
  • 30,372
  • 14
  • 101
  • 138
5

You can also use componentDidMount(), as this lifecycle method is not run when the page is server-side rendered.

JoeTidee
  • 24,754
  • 25
  • 104
  • 149
5

You could also just use the use-ssr React hook

import useSSR from 'use-ssr'

const App = () => {
  var { isBrowser, isServer } = useSSR()

  // Want array destructuring? You can do that too!
  var [isBrowser, isServer] = useSSR()

  /*
   * In your browser's chrome devtools console you should see
   * > IS BROWSER: 
   * > IS SERVER: 
   *
   * AND, in your terminal where your server is running you should see
   * > IS BROWSER: 
   * > IS SERVER: 
   */
  console.log('IS BROWSER: ', isBrowser ? '' : '')
  console.log('IS SERVER: ', isServer ? '' : '')
  return (
    <>
      Is in browser? {isBrowser ? '' : ''}
      <br />
      Is on server? {isServer ? '' : ''}
    </>
  )
}

Example

Alex Cory
  • 10,635
  • 10
  • 52
  • 62
2

You can check if global window variable is defined or not. as in browser it should always be defined.

var isBrowser = window!==undefined
Ramesh Pareek
  • 1,601
  • 3
  • 30
  • 55
0

At the topmost level of the server Element hierarchy, one could add a ServerContext such as this:

class ServerContext extends React.Component {
  getChildContext() { return { isServer: true }; }
  render() { return React.Children.only(this.props.children); }
}

ServerContext.propTypes = {
  children: React.PropTypes.node.isRequired,
};

ServerContext.childContextTypes = {
  isServer: React.PropTypes.bool.isRequired,
};

// Create our React application element.
const reactAppElement = (
  <ServerContext>
    <CodeSplitProvider context={codeSplitContext}>
      <ServerRouter location={request.url} context={reactRouterContext}>
        <DemoApp />
      </ServerRouter>
    </CodeSplitProvider>
  </ServerContext>
);

Doing so, it should be possible to read the isServer from the context like this:

const Layout = (_, { isServer }) => (
  // render stuff here
);
JoeTidee
  • 24,754
  • 25
  • 104
  • 149
0

You can create one useful utility with the help of the exenv package.

import { canUseDOM } from 'exenv';

export function onClient(fn: (..._args: any[]) => any): (..._args: any[]) => any {
    if (canUseDOM) {
        return fn;
    }

    if (process.env.NODE_ENV === 'development') {
        console.log(`Called ${fn.name} on client side only`);
    }

    return (): void => {};
}

And use it like this

function my_function_for_browser_only(arg1: number, arg2: string) {}

onClient(my_function_for_browser_only)(123, "Hi !");

And the function will only be called on client side, and it will log on server side that this function has been called on client side if you set NODE_ENV=development

(It's typescript, remove types for JS :) )

mortimr
  • 111
  • 4
0
if (typeof window === "undefined") { //client side code }

Without typeof you'll get an error.

Kieran
  • 231
  • 3
  • 5