Next.js: Q: How to execute code client-side only

Created on 5 Jul 2017  ·  19Comments  ·  Source: vercel/next.js

I have a callback route in my application that receives access token from auth process in URL hash fragment. I'd like to parse it and save into redux store. I used to get it through window.location.hash before I switched to next, but this is not available when page renders server-side (also the hash fragment apparently isn't sent to server, so I can't parse it there).

Is there any way to restrict some code to execute only in client-side? Or is there any other way to achieve this?

Most helpful comment

@vanniewelt You can always look at process.browser..

if (process.browser) {
  // client-side-only code
}

edit by @Timer:

@vanniewelt You can always look at typeof window..

if (typeof window !== 'undefined') {
  // client-side-only code
}

All 19 comments

@vanniewelt : Dynamically imported Async component loads on client and you can put all client side logic there.

Also ComponentWillReceiveProps lifecycle will receive request in props while at server side.
Can put check if this variable exists and inject your client code.

If the lib needs window variable, i suggest you use Dynamic imports, works well for me.

https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import

You can also use the componentDidMount lifecycle method of React. It's not called server side.

@vanniewelt You can always look at process.browser..

if (process.browser) {
  // client-side-only code
}

edit by @Timer:

@vanniewelt You can always look at typeof window..

if (typeof window !== 'undefined') {
  // client-side-only code
}

yes but it's not the best approach.
let's say I want to check if user is authenticated, and I want to run that check only on client side for several reason, one of them is because I'm storing JWT in localStorage and don't want to store it in a cookie.
So in this case on first page load with :

if (process.browser) {
  // client-side-only code
}

code within the scope won't be executed at all, not on server not on client, it will be executed only if rout change was triggered from client

@sarkistlt You may be right in some cases depending on where that code is placed.. but..

If that code is executed on the server, execution will not reach // client-side-only code. If that code is executed on the client, execution will reach // client-side-only code.

It seems like a valid answer to the question:

Is there any way to restrict some code to execute only in client-side?

@sarkistlt componentDidMount is only executed client-side, componentWillMount is executed both client and server side.

@sarkistlt https://reactjs.org/docs/react-component.html#componentwillmount

This is the only lifecycle hook called on server rendering.

I did it a lot of times, you are confusing componentWillMount with componentDidMount.

@sergiodxa actually you right, have just run the test. thanks!

@sergiodxa componentWillMount's link is changed to https://reactjs.org/docs/react-component.html#unsafe_componentwillmount.

I required mapbox-glmodule which is a client-side library inside ComponentDidMount and got rid of an ugly error ReferenceError: self is not defined

I wasn't able to make Mapbox using componentDidMount because the component still tries to be processed (I don't know what exactly is trying to do) on the server (I ended up having the same issue that @elaich).

Using Dynamic Imports as suggested by @aga5tya did the trick 🎉.

@vanniewelt : Dynamically imported Async component loads on client and you can put all client side logic there.

Also ComponentWillReceiveProps lifecycle will receive request in props while at server side.
Can put check if this variable exists and inject your client code.

If the lib needs window variable, i suggest you use Dynamic imports, works well for me.

https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import

For those that have run into this thread searching for the solution. The Dynamic Imports link listed above is broken. It has been updated and can be found at Dynamic Imports.

If you have a component that's always supposed to be executed on the client, and don't want to use Dynamic Imports (e.g: people can forget that they need to use them), you can use Hooks (or the equivalent componentDidMount) as follows:

import React, { useEffect, useState } from 'react'

function CreateInteraction ({ input }) {
  const [isComponentMounted, setIsComponentMounted] = useState(false)

  useEffect(() => setIsComponentMounted(true), [])

  if(!isComponentMounted) {
    return null
  }

  return <h1>I'm only executed on the client!</h1>
}

Just to add to some already good answers: My current project does a lot of conditional rendering in both directions, so I use a component for this:

import React, { useEffect, useState } from "react";

export interface ConditionallyRenderProps {
    client?: boolean;
    server?: boolean;
}

const ConditionallyRender: React.FC<ConditionallyRenderProps> = (props) => {
    const [isMounted, setIsMounted] = useState(false);

    useEffect(() => setIsMounted(true), []);

    if(!isMounted && props.client) {
        return null;
    }

    if(isMounted && props.server) {
        return null;
    }

    return props.children as React.ReactElement;
};

export default ConditionallyRender;

And then use it as follows:

<Layout>
    <ConditionallyRender server>
        <p>This is rendered only on server.</p>
    </ConditionallyRender>
    <ConditionallyRender client>
        <p>This is rendered only on client.</p>
    </ConditionallyRender>
</Layout>

If you are using any library that access browser API then you can dynamically import modules with SSR=false as second argument.
const DynamicComponentWithNoSSR = dynamic( () => import('../components/hello3'), { ssr: false } )
https://nextjs.org/docs/advanced-features/dynamic-import

If you are using any library that access browser API then you can dynamically import modules with SSR=false as second argument.
const DynamicComponentWithNoSSR = dynamic( () => import('../components/hello3'), { ssr: false } )
https://nextjs.org/docs/advanced-features/dynamic-import

You will also need to add this import if you want to use "dynamic" import for anyone looking at @yashwant-dangi code!

import dynamic from 'next/dynamic'

@Timer I noticed you edited my comment https://github.com/vercel/next.js/issues/2473#issuecomment-362119102, which I again edited to reflect the original comment and your edit.

  1. Why should we use typeof window !== 'undefined' instead of just process.browser? It's more verbose, so if there's no reason here, I prefer to use process.browser.
  2. Next time you edit a contributor comment, could you make the edit apparent in your comment? First off, it can make the thread confusing, like it did here, where the following comment is a reply to my specific comment. Second off, it's misleading since that's not what I wrote, and also that's not what people read when they made their reaction.

Why should we use typeof window !== 'undefined' instead of just process.browser? It's more verbose, so if there's no reason here, I prefer to use process.browser.

process.browser is non-standard and only available in webpack environment, meaning it's likely to break in the future, e.g. webpack 5 no longer polyfills process.

An alternative to typeof window !== "undefined" could be Object.prototype.isPrototypeOf(window). Based on this post, however, bundles produced by Next.js detect the prior and whatever produces lighter bundles should be considered a win.

Was this page helpful?
0 / 5 - 0 ratings