HTML

This tutorial will guide you through building an HTML SSR application from scratch using the Esmx framework. We'll demonstrate how to create a server-side rendered application through a complete example.

Project Structure

First, let's understand the basic project structure:

1.
2├── package.json         # Project configuration file defining dependencies and scripts
3├── tsconfig.json        # TypeScript configuration file with compilation options
4└── src                  # Source code directory
5    ├── app.ts           # Main application component defining page structure and logic
6    ├── create-app.ts    # Application instance factory for initialization
7    ├── entry.client.ts  # Client entry file handling browser-side rendering
8    ├── entry.node.ts    # Node.js server entry file for dev environment setup
9    └── entry.server.ts  # Server entry file handling SSR rendering logic

Project Configuration

package.json

Create the package.json file to configure project dependencies and scripts:

package.json
1{
2  "name": "ssr-demo-html",
3  "version": "1.0.0",
4  "type": "module",
5  "private": true,
6  "scripts": {
7    "dev": "esmx dev",
8    "build": "npm run build:dts && npm run build:ssr",
9    "build:ssr": "esmx build",
10    "preview": "esmx preview",
11    "start": "NODE_ENV=production node dist/index.mjs",
12    "build:dts": "tsc --declaration --emitDeclarationOnly --outDir dist/src"
13  },
14  "dependencies": {
15    "@esmx/core": "*"
16  },
17  "devDependencies": {
18    "@esmx/rspack": "*",
19    "@types/node": "22.8.6",
20    "typescript": "^5.7.3"
21  }
22}

After creating the package.json file, install project dependencies using any of these commands:

1pnpm install
2# or
3yarn install
4# or
5npm install

This will install all required dependencies including TypeScript and SSR-related packages.

tsconfig.json

Create the tsconfig.json file to configure TypeScript compilation:

tsconfig.json
1{
2    "compilerOptions": {
3        "module": "ESNext",
4        "moduleResolution": "node",
5        "isolatedModules": true,
6        "resolveJsonModule": true,
7        
8        "target": "ESNext",
9        "lib": ["ESNext", "DOM"],
10        
11        "strict": true,
12        "skipLibCheck": true,
13        "types": ["@types/node"],
14        
15        "experimentalDecorators": true,
16        "allowSyntheticDefaultImports": true,
17        
18        "baseUrl": ".",
19        "paths": {
20            "ssr-demo-html/src/*": ["./src/*"],
21            "ssr-demo-html/*": ["./*"]
22        }
23    },
24    "include": ["src"],
25    "exclude": ["dist", "node_modules"]
26}

Source Code Structure

app.ts

Create the main application component src/app.ts implementing page structure and interaction logic:

src/app.ts
1/**
2 * @file Example component
3 * @description Demonstrates a page title with auto-updating time, showcasing basic Esmx framework functionality
4 */
5
6export default class App {
7    /**
8     * Current time in ISO format
9     * @type {string}
10     */
11    public time = '';
12
13    /**
14     * Creates application instance
15     * @param {SsrContext} [ssrContext] - Server context containing import metadata collection
16     */
17    public constructor(public ssrContext?: SsrContext) {
18        // No additional initialization needed in constructor
19    }
20
21    /**
22     * Renders page content
23     * @returns {string} Returns page HTML structure
24     */
25    public render(): string {
26        // Ensure proper import metadata collection in server environment
27        if (this.ssrContext) {
28            this.ssrContext.importMetaSet.add(import.meta);
29        }
30
31        return `
32        <div id="app">
33            <h1><a href="https://www.esmnext.com/guide/frameworks/html.html" target="_blank">Esmx Quick Start</a></h1>
34            <time datetime="${this.time}">${this.time}</time>
35        </div>
36        `;
37    }
38
39    /**
40     * Client-side initialization
41     * @throws {Error} Throws error when time display element is not found
42     */
43    public onClient(): void {
44        // Get time display element
45        const time = document.querySelector('#app time');
46        if (!time) {
47            throw new Error('Time display element not found');
48        }
49
50        // Set interval to update time every second
51        setInterval(() => {
52            this.time = new Date().toISOString();
53            time.setAttribute('datetime', this.time);
54            time.textContent = this.time;
55        }, 1000);
56    }
57
58    /**
59     * Server-side initialization
60     */
61    public onServer(): void {
62        this.time = new Date().toISOString();
63    }
64}
65
66/**
67 * Server context interface
68 * @interface
69 */
70export interface SsrContext {
71    /**
72     * Import metadata collection
73     * @type {Set<ImportMeta>}
74     */
75    importMetaSet: Set<ImportMeta>;
76}

create-app.ts

Create src/create-app.ts file responsible for application instance creation:

src/create-app.ts
1/**
2 * @file Application instance creation
3 * @description Responsible for creating and configuring application instances
4 */
5
6import App from './app';
7
8export function createApp() {
9    const app = new App();
10    return {
11        app
12    };
13}

entry.client.ts

Create client entry file src/entry.client.ts:

src/entry.client.ts
1/**
2 * @file Client entry file
3 * @description Handles client-side interaction logic and dynamic updates
4 */
5
6import { createApp } from './create-app';
7
8// Create application instance and initialize
9const { app } = createApp();
10app.onClient();

entry.node.ts

Create entry.node.ts file for development environment configuration:

src/entry.node.ts
1/**
2 * @file Node.js server entry file
3 * @description Configures development environment and server startup, providing SSR runtime
4 */
5
6import http from 'node:http';
7import type { EsmxOptions } from '@esmx/core';
8
9export default {
10    /**
11     * Configures development environment application creator
12     * @description Creates and configures Rspack application instance for development builds and HMR
13     * @param esmx Esmx framework instance providing core functionality
14     * @returns Configured Rspack application instance supporting HMR and live preview
15     */
16    async devApp(esmx) {
17        return import('@esmx/rspack').then((m) =>
18            m.createRspackHtmlApp(esmx, {
19                config(context) {
20                    // Customize Rspack compilation configuration here
21                }
22            })
23        );
24    },
25
26    /**
27     * Configures and starts HTTP server
28     * @description Creates HTTP server instance with Esmx middleware for SSR requests
29     * @param esmx Esmx framework instance providing middleware and rendering
30     */
31    async server(esmx) {
32        const server = http.createServer((req, res) => {
33            // Process requests with Esmx middleware
34            esmx.middleware(req, res, async () => {
35                // Perform server-side rendering
36                const rc = await esmx.render({
37                    params: { url: req.url }
38                });
39                res.end(rc.html);
40            });
41        });
42
43        server.listen(3000, () => {
44            console.log('Server started: http://localhost:3000');
45        });
46    }
47} satisfies EsmxOptions;

This file serves as the entry point for development environment configuration and server startup, containing two core functions:

  1. devApp: Creates and configures Rspack application instance for development with HMR support.
  2. server: Creates and configures HTTP server with Esmx middleware for SSR requests.

entry.server.ts

Create server-side rendering entry file src/entry.server.ts:

src/entry.server.ts
1/**
2 * @file Server-side rendering entry file
3 * @description Handles SSR process, HTML generation and resource injection
4 */
5
6import type { RenderContext } from '@esmx/core';
7import type App from './app';
8import type { SsrContext } from './app';
9import { createApp } from './create-app';
10
11// Encapsulates page content generation logic
12const renderToString = (app: App, ssrContext: SsrContext): string => {
13    // Inject server context into application instance
14    app.ssrContext = ssrContext;
15    // Initialize server-side
16    app.onServer();
17
18    // Generate page content
19    return app.render();
20};
21
22export default async (rc: RenderContext) => {
23    // Create application instance
24    const { app } = createApp();
25    // Generate page content using renderToString
26    const html = renderToString(app, {
27        importMetaSet: rc.importMetaSet
28    });
29
30    // Commit dependency collection to ensure all required resources are loaded
31    await rc.commit();
32
33    // Generate complete HTML structure
34    rc.html = `<!DOCTYPE html>
35<html lang="en">
36<head>
37    ${rc.preload()}
38    <title>Esmx Quick Start</title>
39    ${rc.css()}
40</head>
41<body>
42    ${html}
43    ${rc.importmap()}
44    ${rc.moduleEntry()}
45    ${rc.modulePreload()}
46</body>
47</html>
48`;
49};

Running the Project

After completing all configurations, use these commands to run the project:

  1. Development mode:
1npm run dev
  1. Build project:
1npm run build
  1. Production environment:
1npm run start

Congratulations! You've successfully created an HTML SSR application using Esmx framework. Visit http://localhost:3000 to see the result.