logo

From Angular to Remix: Route by route migration

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": []
    },
"server": {
    "builder": "@angular-devkit/build-angular:server",
    "options": {
        "outputPath": "../build/server/angular",
        "main": "server.ts",
        "tsConfig": "tsconfig.server.json"
    },

Also update outDir in tsconfig.ts:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "../build/out-tsc",

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/",
};

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(
    '/browser', // remix.config.js public path
    express.static('../build/browser/remix'), { // remix.config.js assetsBuildDirectory
        immutable: true,
        maxAge: '1y',
        })
    );

Also add server side scripts:

server.get(
    '/remix*',// Path or URL that should resolve to remix
    createRequestHandler({
        build: require('../build/server/remix'),// remix.config.js serverBuildPath
    })
);

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;
}

Not to switch easily between two frameworks for the users while navigating between application we will use anchor tag as follow: 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">
                    <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>

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>
              <a
                href="/remix"
                class="text-base font-medium text-white hover:text-indigo-50"
              >
                Remix Application
              </a>
            </div>
          </div>
        </div>
    </nav>
</header>

Source code: GitHub

Learn more

Remix

Angular

If you liked this, follow me on Twitter