mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.69k stars 32.23k forks source link

Improve Remix support #39765

Open samuelsycamore opened 11 months ago

samuelsycamore commented 11 months ago

I've started experimenting with using Material UI and Joy UI components with Remix and I've run into a few bugs and blockers along the way. Browsing the issues here, I see several open threads where folks are discussing the same problems I've bumped into, so I thought it might be helpful to create an umbrella issue to bring all those discussions to one centralized, up-to-date location where folks can track the progress.

Here are all of the open issues I could find—some are overlapping, others are probably pretty outdated and possibly no longer relevant but I haven't dug too deeply into each one:

Open

Problems

Here are the biggest things I see after reviewing these issues:

Potential solutions

Examples and docs

There are several things we could do to improve the documentation around Remix + MUI!

If there are any other known issues or requests to improve the DX, please share them here!

arunmmanoharan commented 11 months ago

@samuelsycamore I have MUI working with renderToPipeableStream using this sample:

https://github.com/remix-run/examples/blob/main/chakra-ui/app/entry.server.tsx

yehudamakarov commented 11 months ago

I have this issue where if I import like so: import { useDemoData } from "@mui/x-data-grid-generator"; I get

Uncaught TypeError: Cannot read properties of undefined (reading 'A400')
    at theme.ts:25:17
(anonymous) @ theme.ts:25

The same problem came up for me before and I solved it by changing

import { Alert, Grid } from "@mui/material";

to

import Alert from "@mui/material/Alert";
import Grid from "@mui/material/Grid";

Basically to reproduce,

  1. use the remix material UI example
  2. make a new route
  3. paste in an example from Material UI datagrid that uses useDemoData
rahad06 commented 10 months ago

the provided remix template is old and not compatible, packages and layout is obsolete. please provide an up-to-date remix admin template for MUI with internationalization

mbarni99 commented 10 months ago

@samuelsycamore I have MUI working with renderToPipeableStream using this sample:

https://github.com/remix-run/examples/blob/main/chakra-ui/app/entry.server.tsx

Hello,

I have just tried this one out and it may be outdated as I got the following error: Module '"@remix-run/node"' has no exported member 'Response'.

I skipped importing that Response (whatever it is) and tried with the base FetchAPI Response class, and got the following error: Argument of type 'ReadWriteStream' is not assignable to parameter of type 'BodyInit | null | undefined'.

The full code of trying this one is the following

import { CacheProvider as EmotionCacheProvider } from '@emotion/react';
import { createReadableStreamFromReadable } from '@remix-run/node';
import { PassThrough } from 'node:stream';
import { RemixServer } from '@remix-run/react';
import { renderToPipeableStream } from 'react-dom/server';
import createEmotionCache from '@emotion/cache';
import createEmotionServer from '@emotion/server/create-instance';
import isbot from 'isbot';
import type { AppLoadContext, EntryContext } from '@remix-run/node';

const ABORT_DELAY = 5000;

export default function handleRequest(
    request: Request,
    responseStatusCode: number,
    responseHeaders: Headers,
    remixContext: EntryContext,
    // This is ignored so we can keep it in the template for visibility.  Feel
    // free to delete this parameter in your app if you're not using it!
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    loadContext: AppLoadContext
) {
    return isbot(request.headers.get('user-agent'))
        ? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext)
        : handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext);
}

function handleBotRequest(request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext) {
    return new Promise((resolve, reject) => {
        const emotionCache = createEmotionCache({ key: 'css' });
        let shellRendered = false;

        const { pipe, abort } = renderToPipeableStream(
            <EmotionCacheProvider value={emotionCache}>
                <RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />
            </EmotionCacheProvider>,
            {
                onAllReady() {
                    shellRendered = true;

                    const body = new PassThrough();
                    const emotionServer = createEmotionServer(emotionCache);
                    // const stream = createReadableStreamFromReadable(body);

                    const bodyWithStyles = emotionServer.renderStylesToNodeStream();
                    body.pipe(bodyWithStyles);

                    responseHeaders.set('Content-Type', 'text/html');

                    resolve(
                        new Response(bodyWithStyles, {
                            headers: responseHeaders,
                            status: responseStatusCode,
                        })
                    );

                    pipe(body);
                },
                onShellError(error: unknown) {
                    reject(error);
                },
                onError(error: unknown) {
                    responseStatusCode = 500;
                    // Log streaming rendering errors from inside the shell.  Don't log
                    // errors encountered during initial shell rendering since they'll
                    // reject and get logged in handleDocumentRequest.
                    if (shellRendered) {
                        console.error(error);
                    }
                },
            }
        );

        setTimeout(abort, ABORT_DELAY);
    });
}

function handleBrowserRequest(request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext) {
    return new Promise((resolve, reject) => {
        let shellRendered = false;
        const { pipe, abort } = renderToPipeableStream(<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />, {
            onShellReady() {
                shellRendered = true;
                const body = new PassThrough();
                const stream = createReadableStreamFromReadable(body);

                responseHeaders.set('Content-Type', 'text/html');

                resolve(
                    new Response(stream, {
                        headers: responseHeaders,
                        status: responseStatusCode,
                    })
                );

                pipe(body);
            },
            onShellError(error: unknown) {
                reject(error);
            },
            onError(error: unknown) {
                responseStatusCode = 500;
                // Log streaming rendering errors from inside the shell.  Don't log
                // errors encountered during initial shell rendering since they'll
                // reject and get logged in handleDocumentRequest.
                if (shellRendered) {
                    console.error(error);
                }
            },
        });

        setTimeout(abort, ABORT_DELAY);
    });
}
Brocktho commented 9 months ago

@mbarni99

...I got the following error: Module '"@remix-run/node"' has no exported member 'Response'.

I skipped importing that Response (whatever it is) and tried with the base FetchAPI Response class, and got the following error: >Argument of type 'ReadWriteStream' is not assignable to parameter of type 'BodyInit | null | undefined'.

Remix used to ship a specific Response type that allowed you to dump in a ReadWriteStream into the Response, instead of maintaining that full Response object they now maintain a createReadableStreamFromReadable You would do mostly the same as what you had posted, but before slapping it all into the body of the Response you need to put it into the createReadableStreamFromReadable function like:

   // Import
   import { createReadableStreamFromReadable } from "@remix-run/node";
    // Other Code
   //
  //
           resolve(
                    new Response(createReadableStreamFromReadable(stream), {
                        headers: responseHeaders,
                        status: responseStatusCode,
                    })
                );
michael-land commented 9 months ago

Is it possible to use material ui with remix spa mode?

mahmoudmoravej commented 7 months ago

The current Remix example is a bit outdated and hard to integrate with new Remix templates. I created a brand new integration (with Remix 2.8 + Vite). It helps alot in quick and smooth MUI + Remix integration and also addresses some challenges mentioned here.

Headsup! I am not great with Emotions. But just cross checking with current Remix + MU integration example, it seems it is working as expected with no need to use complex cache/style management.

cc @oliviertassinari @mnajdova

khera commented 5 months ago

The current Remix example is a bit outdated and hard to integrate with new Remix templates. I created a brand new integration (with Remix 2.8 + Vite). It helps alot in quick and smooth MUI + Remix integration and also addresses some challenges mentioned here.

Thank you so much for this. I was pounding my head for the last 4 hours until I just found this. Incorporating your MuiProvider.tsx file into my entry files was the trick I needed to incorporate MUI into my Remix app. The current "official" example is impossible to decipher as a newbie to Remix working on the current default template.

mnajdova commented 5 months ago

The current Remix example is a bit outdated and hard to integrate with new Remix templates. I created https://github.com/mahmoudmoravej/remix-mui/pull/1. It helps alot in quick and smooth MUI + Remix integration and also addresses some challenges mentioned here.

@mahmoudmoravej would you like to update the current example we have and open a PR, we can then check what changes you've made.

mahmoudmoravej commented 5 months ago

@mnajdova I did it but I am not sure it will be so helpful, becasue:

I still think the PR I provided above(which simply added MUI on top of the latest version of Remix/Vite template) shows a better understanding of what and how it has been doe.

cuzzlor commented 4 months ago

The official example uses serverModuleFormat: 'cjs' in the remix config which seems to prevent fast hot-reloading of front-end changes.

I used the starter from @mnajdova: https://github.com/mahmoudmoravej/remix-mui and it seems to work very well so far. Much simpler, streaming (defer/suspense/await) seems to work fine, hot-reloading etc all seems to work perfectly.

incarnateTheGreat commented 2 months ago

The current Remix example is a bit outdated and hard to integrate with new Remix templates. I created a brand new integration (with Remix 2.8 + Vite). It helps alot in quick and smooth MUI + Remix integration and also addresses some challenges mentioned here.

Headsup! I am not great with Emotions. But just cross checking with current Remix + MU integration example, it seems it is working as expected with no need to use complex cache/style management.

cc @oliviertassinari @mnajdova

Thank you so much for this! It really helped!

lookitskris commented 1 month ago

The current Remix example is a bit outdated and hard to integrate with new Remix templates. I created a brand new integration (with Remix 2.8 + Vite). It helps alot in quick and smooth MUI + Remix integration and also addresses some challenges mentioned here.

Headsup! I am not great with Emotions. But just cross checking with current Remix + MU integration example, it seems it is working as expected with no need to use complex cache/style management.

cc @oliviertassinari @mnajdova

my guy - if I could tip you I would. Spent a full day going round and round in circles and this example just works!