wldyslw / react-bottom-sheet

React component for iOS- and Android-like bottom sheet
https://wldyslw.github.io/react-bottom-sheet/
MIT License
9 stars 1 forks source link

Usage in SSR environments #4

Closed wldyslw closed 1 year ago

wldyslw commented 1 year ago

As bottom sheet is rendered in a portal internally, using it with SSR frameworks (like Next.js) might be non-straighforward. It cannot be rendered on a server because mounting node haven't been initialized yet, so you have to do it on client only.

You may come up with an initial solution like this:

function App() {
    return typeof window === 'undefined' ? null : <BottomSheet />;
}

But it will cause hydration problem as content rendered on client will differ from the pre-rendered on server one.

You may also think of something more advanced like using Next's dynamic() function with ssr: false parameter:

const LazySheet = dynamic(() => import('@wldyslw/react-bottom-sheet'), { ssr: false });

function App() {
    return <LazySheet />;
}

But this, again, has very frustrating problem: dynamic will replace original ref handler with its custom one, so you won't be able to open sheet programmatically, for instance. Unfortunately, it won't be resolved.

So before I resolve it internally somehow (doesn't look too complicated, or at least I hope so, just a matter of API choice), here's a number of solutions.

  1. Next.js team prepared a great example of implementing modal in their paradigm: link
  2. If you want to use dynamic() anyway, I advise you to create a wrapper around BottomSheet component and then use it:
    
    // BottomSheetWrapper.tsx
    import { type Ref } from 'react';
    import BottomSheet, {
    type BottomSheetRef,
    type BottomSheetProps,
    } from '@wldyslw/react-bottom-sheet';
    import '@wldyslw/react-bottom-sheet/lib/style.css';

const BottomSheetWrapper = ({ sheetRef, ...props }: BottomSheetProps & { sheetRef?: Ref }) => { return <BottomSheet {...props} ref={sheetRef} />; };

export default BottomSheetWrapper;

```jsx
// App.tsx
import dynamic from 'next/dynamic';
import { useRef } from 'react';
import { type BottomSheetRef } from '@wldyslw/react-bottom-sheet';

const LazySheet = dynamic(() => import('./BottomSheetWrapper'), {
    ssr: false,
});

function App() {
    return <LazySheet />;
}