├── public
└── favicon.ico
├── .gitignore
├── app
├── styles
│ ├── index.css
│ └── global.css
├── tsconfig.json
├── routes
│ ├── 404.tsx
│ └── index.tsx
├── entry.client.tsx
├── entry.server.tsx
└── root.tsx
├── .npmrc.example
├── pm2.config.js
├── package.json
├── server.js
├── remix.config.js
└── README.md
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remix-run/starter-express/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package-lock.json
3 |
4 | /.cache/
5 | /build/
6 | /public/build/
7 |
--------------------------------------------------------------------------------
/app/styles/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | * when the user visits this page, this style will apply, when they leave, it
3 | * will get unloaded, so don't worry so much about conflicting styles between
4 | * pages!
5 | */
6 |
--------------------------------------------------------------------------------
/app/styles/global.css:
--------------------------------------------------------------------------------
1 | :focus:not(:focus-visible) {
2 | outline: none;
3 | }
4 |
5 | body {
6 | font-family: sans-serif;
7 | }
8 |
9 | footer {
10 | text-align: center;
11 | color: #ccc;
12 | padding-top: 80px;
13 | }
14 |
--------------------------------------------------------------------------------
/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["DOM", "DOM.Iterable", "ES2019"],
4 | "esModuleInterop": true,
5 | "jsx": "react-jsx",
6 | "moduleResolution": "node",
7 | "target": "es2019",
8 | "strict": true,
9 | "skipLibCheck": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/routes/404.tsx:
--------------------------------------------------------------------------------
1 | import type { MetaFunction } from "@remix-run/react";
2 |
3 | export let meta: MetaFunction = () => {
4 | return { title: "Ain't nothing here" };
5 | };
6 |
7 | export default function FourOhFour() {
8 | return (
9 |
10 |
404
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/.npmrc.example:
--------------------------------------------------------------------------------
1 | # Swap out "" below with the key you get from logging in
2 | # to https://remix.run. If this is a public repo, you'll want to move this
3 | # line into ~/.npmrc to keep it private.
4 | //npm.remix.run/:_authToken=
5 |
6 | # This line tells npm where to find @remix-run packages.
7 | @remix-run:registry=https://npm.remix.run
8 |
--------------------------------------------------------------------------------
/app/entry.client.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from "react-dom";
2 | import { RemixBrowser as Remix } from "@remix-run/react";
3 |
4 | // @types/react-dom says the 2nd argument to ReactDOM.hydrate() must be a
5 | // `Element | DocumentFragment | null` but React 16 allows you to pass the
6 | // `document` object as well. This is a bug in @types/react-dom that we can
7 | // safely ignore for now.
8 | // @ts-ignore
9 | ReactDOM.hydrate(, document);
10 |
--------------------------------------------------------------------------------
/pm2.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | apps: [
3 | {
4 | name: "Express",
5 | script: "server.js",
6 | watch: ["build/assets.json"],
7 | watch_options: {
8 | followSymlinks: false
9 | },
10 | env: {
11 | NODE_ENV: "development"
12 | }
13 | },
14 | {
15 | name: "Remix",
16 | script: "remix run2",
17 | ignore_watch: ["."],
18 | env: {
19 | NODE_ENV: "development"
20 | }
21 | }
22 | ]
23 | };
24 |
--------------------------------------------------------------------------------
/app/entry.server.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOMServer from "react-dom/server";
2 | import { RemixServer as Remix } from "@remix-run/react";
3 | import type { EntryContext } from "@remix-run/node";
4 |
5 | export default function handleRequest(
6 | request: Request,
7 | responseStatusCode: number,
8 | responseHeaders: Headers,
9 | remixContext: EntryContext
10 | ) {
11 | let markup = ReactDOMServer.renderToString(
12 |
13 | );
14 |
15 | return new Response("" + markup, {
16 | status: responseStatusCode,
17 | headers: {
18 | ...Object.fromEntries(responseHeaders),
19 | "Content-Type": "text/html"
20 | }
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "remix-starter-express",
4 | "scripts": {
5 | "postinstall": "npm run build",
6 | "build": "remix build2",
7 | "dev": "pm2-dev pm2.config.js",
8 | "start": "node server.js"
9 | },
10 | "dependencies": {
11 | "@remix-run/express": "^0.15.1",
12 | "@remix-run/node": "^0.15.1",
13 | "@remix-run/react": "^0.15.1",
14 | "compression": "^1.7.4",
15 | "express": "^4.17.1",
16 | "morgan": "^1.10.0",
17 | "react": "^17.0.1",
18 | "react-dom": "^17.0.1",
19 | "react-router-dom": "^6.0.0-beta.0"
20 | },
21 | "devDependencies": {
22 | "@remix-run/dev": "^0.15.1",
23 | "@types/react": "^17.0.1",
24 | "@types/react-dom": "^17.0.0",
25 | "pm2": "^4.5.4",
26 | "typescript": "^4.1.2"
27 | },
28 | "sideEffects": false,
29 | "engines": {
30 | "node": "12"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import type {
2 | MetaFunction,
3 | LinksFunction,
4 | LoaderFunction
5 | } from "@remix-run/react";
6 | import { useRouteData } from "@remix-run/react";
7 |
8 | import stylesUrl from "../styles/index.css";
9 |
10 | export let meta: MetaFunction = () => {
11 | return {
12 | title: "Remix Starter",
13 | description: "Welcome to remix!"
14 | };
15 | };
16 |
17 | export let links: LinksFunction = () => {
18 | return [{ rel: "stylesheet", href: stylesUrl }];
19 | };
20 |
21 | export let loader: LoaderFunction = async () => {
22 | return { message: "this is awesome 😎" };
23 | };
24 |
25 | export default function Index() {
26 | let data = useRouteData();
27 |
28 | return (
29 |
30 |
Welcome to Remix!
31 |
32 | Check out the docs to get
33 | started.
34 |
35 |
Message from the loader: {data.message}
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const compression = require("compression");
3 | const morgan = require("morgan");
4 | const { createRequestHandler } = require("@remix-run/express");
5 |
6 | let app = express();
7 |
8 | // Responses should be served with compression to minimize total network bytes.
9 | // However, where this compression happens can vary wildly depending on your stack
10 | // and infrastructure. Here we are compressing all our Express responses for the
11 | // purpose of this starter repository, but feel free to (re)move it or change it.
12 | app.use(compression());
13 |
14 | app.use(express.static("public"));
15 |
16 | if (process.env.NODE_ENV === "development") {
17 | app.use(morgan("dev"));
18 | }
19 |
20 | app.all(
21 | "*",
22 | createRequestHandler({
23 | build: require("./build"),
24 | getLoadContext() {
25 | // Whatever you return here will be passed as `context` to your loaders
26 | // and actions.
27 | }
28 | })
29 | );
30 |
31 | let port = process.env.PORT || 3000;
32 |
33 | app.listen(port, () => {
34 | console.log(`Express server started on http://localhost:${port}`);
35 | });
36 |
--------------------------------------------------------------------------------
/app/root.tsx:
--------------------------------------------------------------------------------
1 | import type { LinksFunction, LoaderFunction } from "@remix-run/react";
2 | import { Meta, Links, Scripts, useRouteData } from "@remix-run/react";
3 | import { Outlet } from "react-router-dom";
4 |
5 | import stylesUrl from "./styles/global.css";
6 |
7 | export let links: LinksFunction = () => {
8 | return [{ rel: "stylesheet", href: stylesUrl }];
9 | };
10 |
11 | export let loader: LoaderFunction = async () => {
12 | return { date: new Date() };
13 | };
14 |
15 | export default function App() {
16 | let data = useRouteData();
17 |
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export function ErrorBoundary({ error }: { error: Error }) {
38 | return (
39 |
40 |
41 |
42 | Oops!
43 |
44 |
45 |
46 |
App Error
47 |
{error.message}
48 |
49 | Replace this UI with what you want users to see when your app throws
50 | uncaught errors. The file is at app/root.tsx.
51 |
52 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/remix.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /**
3 | * The path to the `app` directory, relative to remix.config.js. Defaults to
4 | * "app". All code in this directory is part of your app and will be compiled
5 | * by Remix.
6 | *
7 | */
8 | appDirectory: "app",
9 |
10 | /**
11 | * A hook for defining custom routes based on your own file conventions. This
12 | * is not required, but may be useful if you have custom/advanced routing
13 | * requirements.
14 | */
15 | // routes(defineRoutes) {
16 | // return defineRoutes(route => {
17 | // route(
18 | // // The URL path for this route.
19 | // "/pages/one",
20 | // // The path to this route's component file, relative to `appDirectory`.
21 | // "pages/one",
22 | // // Options:
23 | // {
24 | // // The path to this route's data module, relative to `dataDirectory`.
25 | // loader: "...",
26 | // // The path to this route's styles file, relative to `appDirectory`.
27 | // styles: "..."
28 | // }
29 | // );
30 | // });
31 | // },
32 |
33 | /**
34 | * The path to the browser build, relative to remix.config.js. Defaults to
35 | * `public/build`. The browser build contains all public JavaScript and CSS
36 | * files that are created when building your routes.
37 | */
38 | browserBuildDirectory: "public/build",
39 |
40 | /**
41 | * The URL prefix of the browser build with a trailing slash. Defaults to
42 | * `/build/`.
43 | */
44 | publicPath: "/build/",
45 |
46 | /**
47 | * The path to the server build directory, relative to remix.config.js.
48 | * Defaults to `build`. The server build is a collection of JavaScript modules
49 | * that are created from building your routes. They are used on the server to
50 | * generate HTML.
51 | */
52 | serverBuildDirectory: "build",
53 |
54 | /**
55 | * The port to use when running `remix run`. Defaults to 8002.
56 | */
57 | devServerPort: 8002
58 | };
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Please Don't Use
2 |
3 | > Please use `npm init remix` instead of this starter repo to create a new Remix app.
4 | > This repository was archived on April 29, 2021.
5 |
6 | # Remix Starter for Express
7 |
8 | Welcome to Remix!
9 |
10 | This is a starter repo for using [Remix](https://remix.run) with [Express](http://expressjs.com/).
11 |
12 | ## Development
13 |
14 | After cloning the repo, rename `.npmrc.example` to `.npmrc` and insert the license key you get from [logging in to your dashboard at remix.run](https://remix.run).
15 |
16 | > Note: if this is a public repo, you'll probably want to move the line with your key into `~/.npmrc` to keep it private.
17 |
18 | Then, install all dependencies using `npm`:
19 |
20 | ```sh
21 | $ npm install
22 | ```
23 |
24 | Your `@remix-run/*` dependencies will come from the Remix package registry.
25 |
26 | Once everything is installed, start the app in development mode with the following command:
27 |
28 | ```sh
29 | $ npm run dev
30 | ```
31 |
32 | This will run a few processes concurrently that will dynamically rebuild as your source files change. To see your changes, refresh the browser.
33 |
34 | > Note: Hot module reloading is coming soon, which will allow you to see your changes without refreshing.
35 |
36 | ## Production
37 |
38 | To run the app in production mode, you'll need to build it first.
39 |
40 | ```sh
41 | $ npm run build
42 | $ npm start
43 | ```
44 |
45 | This will start a single HTTP server process that will serve the app from the files generated in the build step.
46 |
47 | ## Documentation
48 |
49 | Detailed documentation for Remix [is available at remix.run](https://remix.run/dashboard/docs).
50 |
51 | ## Project Structure
52 |
53 | All application source code is found in the `app` directory. This includes your application entry points for both server rendering (see `app/entry.server.tsx`) and the browser (see `app/entry.client.tsx`), as well as your root component and routes (see `app/root.tsx` and `app/routes`).
54 |
55 | Everything in the `public` directory is served by `express.static`.
56 |
57 | ## Don't want TypeScript?
58 |
59 | The [`no-typescript` branch](https://github.com/remix-run/starter-express/tree/no-typescript) is a version of this same starter template that uses plain JavaScript instead of TypeScript for all code in `app`.
60 |
--------------------------------------------------------------------------------