To begin with, I'm following the Remix "Jokes" app tutorial.
Idea: make it store random facts instead of jokes.
- Remix nuked this
README.mdfile and I had to recreate it. Should have expected that! Adding the contents below instead. - The default
entry.server.tsxfile contains ahandleRequestfunction that takes arequest. That's fine, but it also takes aresponseStatusCodeandresponseHeaders. Am I still able to decide what status code to respond with? This signature is a bit weird to me. - Since Remix picks up on certain exports from files (like
export const meta) but my IDE doesn't know about that, I'm getting a bunch of linter errors on those lines. I'm not sure where the errors are coming from, since from my recollectioneslintdoesn't complain about unused exports. It has no knowledge of the other files. It's probably IntelliJ then. - The default
root.tsxcontains several interesting elements:<Outlet />which is like a Svelte<slot />, as far as I remember.<ScrollRestoration />, probably aptly-named. Its presence surprises me because this used to be a feature ofreact-routerthat got dropped after the developers noticed scroll restoration working increasingly better out of the box on major browsers.- To-do: find out how
<ScrollRestoration />is implemented. - Remix documentation on scroll restoration mentions that this component works
by restoring the scroll level before rehydration. This should eliminate the jarring effect of
having the scroll point being restored after the page loads completely.
- The "before rehydration" part is true only by virtue of the
<ScrollRestoration />component being used one line before the<Scripts />component.
- The "before rehydration" part is true only by virtue of the
- To-do: find out how
- The
yarn buildcommand finished in 0.44 seconds. Nice! - I like that the default app doesn't contain images or marketing text selling Remix. It just has some documentation links.
- While adding some code and missing an import, the app showed me a build error (expectedly). But then after fixing the error, the app didn't go back to a normal state, and the error remained, even though the build didn't show the error any longer.
- Adding the new routes, I'm thinking that the structure where
facts/newshares a namespace withfacts/$factIdis not great. What if I wanted slugs instead of IDs, and someone created a fact namednew? Other solutions aren't as pretty, though:facts/show/$factId + facts/new, ornew-fact + facts/$factId. - By exporting a
linksarray, you can add<link>elements to<head>sort of like withreact-helmet. They then get picked up by a top-level<Links />component. - The tutorial recommends a file structure for styles that's not great. If the CSS I write is
encapsulated together with a route, then why shouldn't their files live together? Having a
separate
stylesdirectory adds quite a bit of indirection and could make a project hard to navigate. - I'm skipping all the Prisma stuff, I think it doesn't fit the purposes of a Remix-specific
tutorial. Faking my own database with
asyncfunctions. - I wonder why the tutorial recommends using
export let. It looks to me as if the things I'm exporting shouldn't ever be reassigned. I'm changing these toexport const, hoping that nothing explodes. - The
useDataLoaderpattern with anexport const loadercoupled with auseDataLoadercall ( imported fromremix) feels a bit weird to me. I suppose there must be quite a bit going on at build time for this to be worth the indirection. My first impression is that if it shouldn't be necessary? Why can't I goexport const loader = someRemixUtility(async () => {})instead ( removing theuseDataLoadercall inside the component)? The client and server bundles can still provide different implementations ofsomeRemixUtility.- To-do: find out why
export const loadercan't be used directly and needs to be accessed viauseDataLoader. My guess: the purpose is to call the loader during server rendering and then to reuse the same data during rehydration, to initialize a frontend cache with. Remix developers simply decided to for a standard way to access Remix functionality, and this just looks consistent.
- To-do: find out why
- There's another insidious result of the indirection introduced by the
useDataLoaderandexport const loaderpattern, aggravated by the fact that the suggested typeLoaderFunctionisn't generic: discrepancies between whatloaderactually returns and whatuseDataLoaderreturns aren't going to be caught unless a type is shared between them. ButLoaderFunctionisn't generic, so there's no enforcement from Remix to make sure that this is the case. I can have aloaderthat returnsnumberbut then accessstring[]inuseDataLoaderand unless I actively share the types, TypeScript won't have a chance to complain. In my opinion,LoaderFunctionshould take a required type argument for the return type, and another ( perhaps optional?) for query parameters.- One could argue that this is the responsibility of the developer, and it is, but by not requiring any type arguments, Remix isn't helping.
- It's also inconsistent because
LoaderFunctiontakes no type arguments butuseDataLoadertakes a type argument for the returned data.
- When submitting data, the type mismatch is more accentuated, because
POSTrequests containFormData, which bears (as far as I know) no information on the shape of the data from the form. In this case, however, it matters less, because the tutorial calls the user to perform backend validation/parsing of this form data, by goingform.get("field-name")and then validating the result.- I wonder whether building a way to type-check JSX forms and providing e.g. a TS
eslintplug-in that uses the full power of the AST to build type-safe forms would be worth it. I suppose exporting a simple component for use,TypedForm<T>would be enough, or perhaps even more magic, somehow simply type-check<form>elements based on their current children. This is probably not possible because one would need to go into other modules. Maybe theTypedForm<T>approach is good as long as one sharesTwith child controls.
- I wonder whether building a way to type-check JSX forms and providing e.g. a TS
From your terminal:
npm run devThis starts your app in development mode, rebuilding assets on file changes.
First, build your app for production:
npm run buildThen run the app in production mode:
npm startNow you'll need to pick a host to deploy it to.
If you're familiar with deploying node applications, the built-in Remix app server is production-ready.
Make sure to deploy the output of remix build
build/public/build/
When you ran npx create-remix@latest there were a few choices for hosting. You can run that again
to create a new project, then copy over your app/ folder to the new project that's pre-configured
for your target server.
cd ..
# create a new project, and pick a pre-configured host
npx create-remix@latest
cd my-new-remix-app
# remove the new project's app (not the old one!)
rm -rf app
# copy your app over
cp -R ../my-old-remix-app/app app