Practical Methods for Passing Variables from EJS Templates to Client-Side JavaScript

Dec 11, 2025 · Programming · 7 views · 7.8

Keywords: EJS templating | JavaScript variable passing | Node.js Express

Abstract: This article explores how to securely and effectively pass server-side variables to client-side JavaScript logic in Node.js Express applications using the EJS templating engine. By analyzing two main methods—direct injection and JSON serialization—it details implementation steps, security considerations, and applicable scenarios. Based on real-world Q&A cases and EJS features, the article provides complete code examples and best practice recommendations to help developers achieve efficient and reliable communication between front-end and back-end data interactions.

Introduction

In modern web development, Node.js and the Express framework are often combined with the EJS templating engine to build dynamic applications. A common requirement is to pass server-side data (such as game states, user information, etc.) to client-side JavaScript to enable interactive functionalities. This article is based on a specific case: in a game, when accessing a room, the gameState variable needs to be passed from an Express route to an EJS template and then imported into client-side JavaScript logic. We will delve into methods to achieve this goal, focusing on the best answer and supplementing with other viable solutions.

Core Problem Analysis

In an Express application, when a user accesses a route like /room/:name/:roomId, the server retrieves the game state via a database query and renders an EJS template using res.render(). For example:

server.get("/room/:name/:roomId", function (req, res) {
  game = ~databaseLookup~
  res.render("board", { gameState : game.gameState });
});

In the EJS template board.ejs, gameState can be used directly for conditional rendering:

<% if (gameState) { %>
  <h2>I have a game state!</h2>
<% } %>

However, if this variable needs to be manipulated in client-side JavaScript (e.g., accessing piece positions to display the board correctly), it must be transferred from the EJS environment to the browser side. This involves data serialization and safe injection, which is the key to this problem.

Method 1: Direct Variable Injection

The best answer proposes a simple and effective method: directly inject the variable into JavaScript via a <script> tag in the EJS template. The specific implementation is as follows:

<% if (gameState) { %>
     <h2>I have a game state!</h2>
     <script>
        var clientGameState = <%= gameState %>            
     </script>
<% } %>

Here, <%= gameState %> uses the EJS output tag to embed the value of gameState directly into the JavaScript code. Assuming gameState is a JavaScript object (e.g., { pieces: [{x: 1, y: 2}] }), the rendered HTML will include:

<script>
    var clientGameState = { pieces: [{x: 1, y: 2}] };            
</script>

Thus, client-side JavaScript can directly access the clientGameState variable. This method is advantageous due to its simplicity and lack of additional network requests. However, note that if gameState contains special characters (such as quotes or newlines), it may break JavaScript syntax and cause errors. Therefore, ensuring the data is a safe JSON object is crucial. In Express, JSON.stringify() is often used to preprocess data, but in this example, EJS automatically handles basic types.

Method 2: JSON Serialization and Parsing

A supplementary answer provides another method, particularly suitable for complex data structures (such as arrays or nested objects). By outputting an unescaped JSON string via the EJS <%- %> tag and then parsing it on the client side:

<script>
  var test = '<%- JSON.stringify(sampleJsonData) %>'; // test is now a valid js object
</script>

Here, JSON.stringify(sampleJsonData) converts server-side data into a JSON string, and <%- %> ensures the output is not HTML-escaped. Single quotes are used to wrap the string to avoid conflicts with double quotes in the JSON. Client-side JavaScript can obtain the object via JSON.parse(test) or direct assignment (if the string format is correct). For example, if sampleJsonData is { name: "Game" }, the rendered result is:

<script>
  var test = '{"name":"Game"}';
  var obj = JSON.parse(test); // obj is a JavaScript object
</script>

This method is safer and can handle arbitrary JSON data, but adds a parsing step. According to the EJS documentation, <%- outputs unescaped values, suitable for embedding raw HTML or script content.

Security and Best Practices

When implementing the above methods, consider the following security aspects:

  1. Data Validation: Ensure that the gameState retrieved from the database does not contain malicious scripts to prevent XSS attacks. In Express, middleware can be used to filter inputs.
  2. Escape Handling: If the data contains HTML special characters (such as < or >), they should be escaped before injection. For example, the <%= tag automatically escapes, but <%- does not, requiring manual handling.
  3. Error Handling: In client-side JavaScript, check if variables exist to avoid undefined errors. For example:
    if (typeof clientGameState !== 'undefined') {
        console.log(clientGameState);
    }

Best practice recommendations: Use Method 1 for simple data; for complex or untrusted data, prefer Method 2 combined with server-side validation. Additionally, consider using AJAX calls (as mentioned in the best answer) to dynamically fetch data after page load, improving flexibility but increasing network latency.

Code Examples and Integration

Below is a complete example demonstrating how to pass data in an Express route and implement both methods in an EJS template:

// server.js - Express route
const express = require('express');
const app = express();
app.set('view engine', 'ejs');

app.get("/room/:name/:roomId", function (req, res) {
    // Simulate database query
    const game = {
        gameState: {
            pieces: [{ x: 0, y: 0 }, { x: 1, y: 1 }],
            score: 100
        }
    };
    res.render("board", { gameState: game.gameState });
});

app.listen(3000, () => console.log('Server running'));
<!-- board.ejs -->
<!DOCTYPE html>
<html>
<head>
    <title>Game Board</title>
</head>
<body>
    <% if (gameState) { %>
        <h2>Game State Loaded</h2>
        <!-- Method 1: Direct injection -->
        <script>
            var clientGameState = <%= JSON.stringify(gameState) %>;
            console.log('Direct injection:', clientGameState);
        </script>
        <!-- Method 2: JSON serialization -->
        <script>
            var serializedState = '<%- JSON.stringify(gameState) %>';
            var parsedState = JSON.parse(serializedState);
            console.log('Serialized and parsed:', parsedState);
        </script>
    <% } else { %>
        <p>No game state available.</p>
    <% } %>
    <script src="gameLogic.js"></script>
</body>
</html>

In gameLogic.js, clientGameState or parsedState can be accessed to implement game logic, such as rendering the board.

Conclusion

Through this discussion, we have explored multiple methods for passing server-side variables from EJS templates to client-side JavaScript. Direct injection is suitable for simple, trusted data, while JSON serialization offers higher security and flexibility. In practical development, choose the appropriate solution based on data complexity and security requirements, and combine it with Express data processing capabilities to ensure applications are both efficient and reliable. These techniques are not only applicable to game development but can also be extended to other scenarios requiring front-end and back-end data interaction.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.