DEV Community

Cover image for From Angular to Remix: Route by route migration
Habib Hinn
Habib Hinn

Posted on • Updated on

From Angular to Remix: Route by route migration

In this tutorial, we will show you how to migrate an Angular application to Remix by running Angular Universal and Remix projects side by side on the same Express.js server. We will provide an example, source code, and screenshots to help you understand the process.

An example of the final application that contains Angular Universal and Remix running side by side on the same ExpressJS server can be found at: https://remix-angular.habibhinn.com/

When we run build commands for either framework, it will generate the server-side scripts and the client-side scripts. These scripts handle the rendering of the application on the server and the transfer of the rendered HTML to the client.

To start, we will use npm workspaces to manage multiple projects in the same repo. Create two subfolders - one for the Angular source code and one for the Remix source code. Also, we will create a build folder that contains the output bundle for both projects.

Project structure

In order to change the Angular output build path, we need to update the angular.json file for both the build and server commands.

"build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
        "outputPath": "../build/browser/angular",
        "index": "src/index.html",
        "main": "src/main.ts",
        "polyfills": ["zone.js"],
        "tsConfig": "tsconfig.app.json",
        "assets": ["src/favicon.ico", "src/assets"],
        "styles": ["src/styles.css"],
        "scripts": []
    },
Enter fullscreen mode Exit fullscreen mode
"server": {
    "builder": "@angular-devkit/build-angular:server",
    "options": {
        "outputPath": "../build/server/angular",
        "main": "server.ts",
        "tsConfig": "tsconfig.server.json"
    },
Enter fullscreen mode Exit fullscreen mode

Also update outDir in tsconfig.ts:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "../build/out-tsc",
Enter fullscreen mode Exit fullscreen mode

Also in Remix, we will update remix.config.js:

/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
    ignoredRouteFiles: ["**/.*"],
    appDirectory: "app",
    assetsBuildDirectory: "../build/browser/remix",
    serverBuildPath: "../build/server/remix/index.js",
    publicPath: "/browser/",
};
Enter fullscreen mode Exit fullscreen mode

Now, you will need to configure Express.js server to handle both client side javascript and server side scripts for both frameworks. In this tutorial we will update angular server to serve remix routes. (It can be done the other way)

Add to server.ts to handle client side scripts:

server.use(
    // Note: must match remix.config.js publicPath value
    '/browser', 
    // Note: must match remix.config.js assetsBuildDirectory value
    express.static('../build/browser/remix'), { 
        immutable: true,
        maxAge: '1y',
        })
    );
Enter fullscreen mode Exit fullscreen mode

Also add server side scripts:

server.get(
     // Path or URL that should resolve to remix
    '/remix*',
     // Note: must match remix.config.js assetsBuildDirectory value
    createRequestHandler({
                // remix.config.js serverBuildPath
        build: require('../build/server/remix'),
    })
);
Enter fullscreen mode Exit fullscreen mode

so the full code for the server.ts file should be:

export function app(): express.Express {
    const server = express();
    const distFolder = join(BROWSER_FILES_BASE_PATH, 'angular');
    const indexHtml = existsSync(join(distFolder, 'index.original.html'))
            ? 'index.original.html'
            : 'index';

    // Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
    server.engine(
        'html',
        ngExpressEngine({
        bootstrap: AppServerModule,
        })
    );

    server.set('view engine', 'html');
    server.set('views', distFolder);

    server.use(
        '/browser',
        express.static(join(BROWSER_FILES_BASE_PATH, 'remix'), {
          immutable: true,
          maxAge: '1y',
        })
      );

    server.get(
        '/remix*',
        createRequestHandler({
          build: require('../build/server/remix'),
        })
      );

    // Example Express Rest API endpoints
    // server.get('/api/**', (req, res) => { });
    // Serve static files from /browser
    server.get(
        '*.*',
        express.static(distFolder, {
            maxAge: '1y',
        })
    );

    // All regular routes use the Universal engine
    server.get('*', (req, res) => {
        res.render(indexHtml, {
        req,
        providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
        });
    });

    return server;
}
Enter fullscreen mode Exit fullscreen mode

Not to switch easily between two frameworks for the users while navigating between application we will use anchor tag as it will do a full page refresh.
Remix:

<header className="bg-indigo-600">
    <nav className="mx-auto max-w-7xl px-6 lg:px-8" aria-label="Top">
        <div className="flex w-full items-center justify-between border-b border-indigo-500 py-6 lg:border-none">
            <div className="flex items-center">
                <div className="ml-10 block space-x-8">

           <!-- Defined as anchor tag to do full page refresh -->
             <a
                    href={"/"}
                    className="text-base font-medium text-white hover:text-indigo-50"
                  >
                    Angular Application
                  </a>
                  <Link
                    to="/remix"
                    className="text-base font-medium text-white hover:text-indigo-50"
                  >
                    Remix Application
                  </Link>
                </div>
            </div>
        </div>
    </nav>
</header>
Enter fullscreen mode Exit fullscreen mode

Angular:

<header class="bg-indigo-600">
    <nav class="mx-auto max-w-7xl px-6 lg:px-8" aria-label="Top">
        <div
          class="flex w-full items-center justify-between border-b border-indigo-500 py-6 lg:border-none"
        >
          <div class="flex items-center">
            <div class="ml-10 block space-x-8">
              <a
                routerLink="/"
                class="text-base font-medium text-white hover:text-indigo-50"
              >
                Angular Application
              </a>
              <!-- Defined as anchor tag to do full page refresh -->
              <a
                href="/remix"
                class="text-base font-medium text-white hover:text-indigo-50"
              >
                Remix Application
              </a>
            </div>
          </div>
        </div>
    </nav>
</header>
Enter fullscreen mode Exit fullscreen mode

Source code: GitHub
Original Article: https://habibhinn.com/blog/from-angular-to-remix-route-by-route-migration

Learn more

Remix
Angular

If you liked this, click the ❤️ so other people will see it. Follow me on Twitter and visit my website for more content!

Latest comments (0)