lemmy-ui icon indicating copy to clipboard operation
lemmy-ui copied to clipboard

Migrate from Inferno.js to Next.js (POC)

Open phiresky opened this issue 2 years ago • 5 comments

This is an attempt to make the minimal changes necessary to migrate lemmy-ui to React + Nextjs.

This is a proof of concept - only the front page and the post page load: image

Nextjs is conceptually very similar to Inferno. The structure is a different but so far in this experiment i haven't actually had to change much to make it work.

  • Why?

    • NextJS is an order or two of magnitude more popular than Inferno.js and still growing 2023-12-15-16-25-04 : . This means a larger community, better support, fewer rare bugs, and more devs willing to contribute.
    • It is recommended officially by the React devs for full-stack applications: https://react.dev/learn/start-a-new-react-project
    • It uses react proper / is not a reimplementation so doesn't have any incompatibility issues.
    • It handles a lot of things cleanly by itself that are currently handled in a really ugly and potentially dangerous way. Especially the way window.isoData is handled and the create-ssr-html file.
    • It provides guidelines and documentation for lots of things like handling styling, image resizing, data fetching. It has tons of lints to guide devs in the "right" direction.
    • The dev experience should be better since they have good hot reload, error displaying etc. and are working on lots of tooling like their Rust-based build system https://turbo.build/ and
    • It could come with large performance benefits, one because it's more optimized wrt. pre-rendering static routes and the integrated caching functionalities.
    • In theory it allows interesting things in the future like running on edge runtimes and making the client JS code much slimmer with server-only components.
  • Why Not?

    • React and Nextjs have been doing some things that are a bit opinionated / controversial. Namely, Server Components (not to be confused with SSR Server side rendering) and Server Actions. Hooks used to be controversial but seem to be widely accepted now. My opinion is since they are optional to use it's fine (and good to see new approaches). The only thing I don't understand is why they would choose to override native behaviour which has been known to be a dumb idea for a decade.
    • There's some criticism about Vercel and NextJS: https://www.epicweb.dev/why-i-wont-use-nextjs
    • Nextjs has often changed major things around in the previous major versions. Example here. They do provide fairly good migration guides though, and this can also be seen as a positive that they don't stagnate when new methods of doing things appear.
    • @dessalines has been working on a lemmy-ui replacement written in Rust. The discussion and my opinion on this is here: https://github.com/LemmyNet/lemmy-ui-leptos/issues/15#issuecomment-1839149120
  • How? Done:

    1. removed inferno dependencies completely and replaces it with next.js, react, react-router, react-i18next. import from "@/x" is just an alias for /src/x fyi to not need ../../../../../... imports.
    2. created next.config.js and other config files.
    3. moved static assets into /public for next.js (this works but potentially they should be part of the build step instead.)
    4. created a tiny shim in /src/inferno.ts and /src/inferno-router.ts. These can easily be removed, just wanted to keep the diff smaller.
    5. created the route for / in /app/page.tsx and for /post/id in app/post/[post_id]/page.tsx. These are server components that wrap components/home and components/post and replace the role of routes.ts. I also moved the fetchInitialData methods to them because they are not needed on the client.
    6. replaced all string inline styles with object inline styles (there were not a lot of them)
    7. added declare context: any for now - how to handle that context needs to be investigated
    8. removed a few input id=random() because that breaks hydration. useId() is the replacement but only works in functional components (see src/shared/components/common/listing-type-select.tsx)
    9. removed a few other isBrowser() checks because they break hydration. isBrowser() checks need to happen only in effects (so they run after the hydration has succeeded).

    Todo:

    • all the other routes from routes.ts need to be migrated to /app.
    • react-router should probably be removed and replaced completely with next/router. The nextjs router has a fairly different API (based on directory structure) but it seems to work ok.
    • theming is broken because nextjs doesn't by default like dynamic style imports
    • Sadly, as predicted, a fair amount of react functionality is only available via hooks anymore. This means that e.g. to get the nextjs router with useRouter() the class components have to be wrapped in a functional component. React context which is extensively used should work with class components though.
    • Many components seem to use state when they should probably be using props - deriving state from props is fragile.
    • figure out how to handle the FirstLoadService.isFirstLoad stuff. I think that should be fully removed and moved further outward (handled by the router).
    • not really nextjs related, but UserService should be in the context and not static
    • server-side error handling is not implemented

phiresky avatar Dec 15 '23 16:12 phiresky

If you get everything wired up and working, I wouldn't be against it. But I'll probably devote most of my UI time to lemmy-ui-leptos, and eventually have this repo mostly in maintenance mode.

dessalines avatar Dec 15 '23 16:12 dessalines

Well I think this is probably only good to do with some more weight behind it - I don't personally care enough since I've not spent much time with this code base in any case. So it would need someone else to commit to helping / supporting - I probably don't have the energy to push it to the finish line myself.

Alternatively might also be better to wait for another 6 months or so and see what the state of the leptos UI (as well as other frontends maybe) is. If this repo is still then the thing used by the majority of lemmy core ui development, then it might be worth it. If not - then that's great too.

phiresky avatar Dec 19 '23 22:12 phiresky

To add before spending more time on this: An alternatives to consider is Remix. Seems to be a much thinner thing more based on native Web APIs than nextjs and is popular with people that don't like nextjs https://github.com/remix-run/remix

phiresky avatar Jan 05 '24 18:01 phiresky

I think this is a seriously awesome initiative, I can definitely see it bringing new activity & contributors to this repo.

IMO we shouldn't start a discussion about other alternative frameworks to consider, as it's just a highly subjective topic. NextJS performs well, has a massive userbase, pretty easy learning curve, and is very well maintained, IMO it ticks all the boxes we need, so if we can just lock it in then we can probably save a lot of time by eliminating the inevitable debates about the merits of different frameworks 😛.

If we can agree on this then I would also like to offer some of my time and tackle some of the to-dos here.

sunaurus avatar Jan 08 '24 13:01 sunaurus

If it required very little effort, and was close to a drop-in-replacement, then I'd be for it. But since it looks like it'd be a significant effort, I'd rather not people spend time on it.

Some more discussion on why some of us prefer leptos is here: https://github.com/LemmyNet/lemmy-ui-leptos/issues/15 . Working on lemmy-ui-leptos is probably going to be one of my larger efforts this year.

Of course If someone wants to make a 3rd party lemmy UI in NextJS, using this repo as a base, I'd welcome it too. There are also a lot of 3rd party UIs now built with other frameworks like svelte, (alexandrite and photon), that probably have much cleaner javascript code than this one.

dessalines avatar Jan 09 '24 17:01 dessalines