Custom Scripts
Please note that the learning objective for this part of the exercise is the
use of custom inline scripts. The specific script used is to expose
environment variables following a pattern I still need to write an article
about. If you struggle to follow the pattern, don't worry about that. Focus on
what it takes to get a custom inline script running.
π¦ For this step, you need a little more background info about Remix. In Remix,
you have two "bundles" of JavaScript: the server bundle and the client bundle.
Each of these has its own "entry" file which is the first file that gets
executed when the bundle is loaded. Remix has a default entry file for the
server and the client, but you can override these with your own entry files by
creating a file named
entry.server.tsx
or entry.client.tsx
in your app
directory. We'll be customizing both of these in this exercise, so they've been
added.π¨βπΌ We want to have some custom dev tools for our app (learn about this concept
in Make your own DevTools).
We want to make sure we only load these in development mode. So we need a way
to dynamically load this into the client during development. We determine
whether we're in development based on the environment variable
NODE_ENV
.You may be used to using
process.env
to access environment variables, but the
browser doesn't have access to our server's process.env
(thank goodness).
Instead, we need to explicitly provide those variable's values to our app
using 's loader.π§ββοΈ I added a file which is
responsible for ensuring we have the right environment variables in our server
and makes it easy to get the environment variables that are public and should be
accessible to our UI code.
Remember that UI code runs both on the server and the client. So what we want is
something that allows us to have a common global variable that has all the
environment variables that are public and should be accessible to our UI code.
For example:
function ConnectDiscord() {
return <a href={getDiscordAuthorizeURL()}>Connect to Discord</a>
}
function getDiscordAuthorizeURL(domainUrl: string) {
const url = new URL('https://discord.com/api/oauth2/authorize')
url.searchParams.set('client_id', ENV.DISCORD_CLIENT_ID)
url.searchParams.set('redirect_uri', `https://example.com/discord/callback`)
url.searchParams.set('response_type', 'code')
url.searchParams.set('scope', 'identify guilds.join email guilds')
return url.toString()
}
π§ββοΈ To do this, I've also created
custom
and files which override the
default entry files for the server and client in Remix.
With the
entry.server.tsx
, we can take care of the server-side of setting this
global variable which I have done on line 6 with:import { getEnv } from './utils/env.server.ts'
global.ENV = getEnv()
The
env.server.ts
file we have sets up TypeScript to auto-complete the ENV
global variable.π¨βπΌ In this part of the exercise, we need to set the global
ENV
variable on the
client. So when our code hydrates we have access to runtime environment
variables using the same global variable in either environment (server or
client).Right now, all we need in our app is a
MODE
environment variable which is
development
or production
.Effectively, what we need is a script that does this:
window.ENV = { MODE: 'development' }