javascript

Client-side vs Server-side JavaScript

Understand the difference between client-side and server-side JavaScript — where each runs, what each can do, and how they work together to build full-stack web applications.

17 min read 11 sections Tutorial
Share

Client-side vs Server-side JavaScript

Client-side vs Server-side JavaScript

JavaScript started as a browser-only language. For its first 14 years, it could only run inside a web browser — on the client side. That changed in 2009 when Node.js was released, making JavaScript capable of running on servers too.

Today, JavaScript is the only language that runs natively on both sides of the web. Understanding the difference between client-side and server-side JS is fundamental to web development.

The Core Difference

Client-side JavaScript runs in the user's browser — on their device. It controls what the user sees and interacts with.

Server-side JavaScript runs on a remote server — in a data center somewhere. It handles databases, business logic, authentication, and sends responses to browsers.

Client-side

Runs in the browser on the user's machine. Controls the UI, responds to clicks, updates the page without reload.

Server-side

Runs on a remote server. Handles databases, authentication, file storage, and business logic.

Full-stack

JavaScript on both sides. One language, one team, shared code — the unique advantage JS has over every other language.

Universal / Isomorphic

Code that runs on both client and server. Next.js and Nuxt are built on this concept.

Client-side JavaScript

Client-side JavaScript

Client-side JavaScript is code that runs in the user's web browser. When a browser loads a webpage, it downloads the HTML, CSS, and JavaScript files. The JavaScript runs locally on the user's device — not on any server.

Where it runs

Client-side JS runs inside the browser's JavaScript engine:

  • Chrome → V8
  • Firefox → SpiderMonkey
  • Safari → JavaScriptCore

The code executes on the user's CPU using the user's memory. The server has nothing to do with it after sending the files.

What client-side JavaScript can do:

Manipulate the DOM

Add, remove, or change HTML elements and CSS styles dynamically without reloading the page.

Handle User Events

Respond to clicks, keyboard input, mouse movement, form submissions, and scroll events.

Make API Requests

Fetch data from servers using fetch() or XMLHttpRequest — load new content without a full page reload.

Use Browser Storage

Read and write localStorage, sessionStorage, IndexedDB, and cookies on the user's device.

Animations & Canvas

Create animations with CSS transitions, Web Animations API, Canvas 2D, and WebGL.

Access Device APIs

Geolocation, camera, microphone, notifications, vibration, and clipboard — with user permission.

What client-side JavaScript CANNOT do:

Browser Security Restrictions

For security reasons, client-side JavaScript is sandboxed — it cannot:

  • Read files on the user's computer (without explicit user selection)
  • Access other browser tabs or windows from a different origin
  • Connect directly to a database (no direct MySQL/MongoDB connection)
  • Send emails or make server-to-server requests hidden from the user
  • Run in the background when the browser tab is closed
  • Access the user's OS beyond what the browser explicitly exposes
html
1<!DOCTYPE html>
2<html>
3<head>
4 <style>
5 body { font-family: sans-serif; padding: 20px; background: #0f172a; color: white; }
6 .box { padding: 20px; background: #1e293b; border-radius: 10px; margin: 10px 0; }
7 button { padding: 10px 20px; margin: 5px; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; }
8 #output { color: #4ade80; font-size: 18px; margin: 10px 0; min-height: 30px; }
9 #storage-display { color: #94a3b8; font-size: 13px; }
10 </style>
11</head>
12<body>
13 <div class="box">
14 <h3>🎨 DOM Manipulation</h3>
15 <div id="output">Click a button!</div>
16 <button onclick="changeText()" style="background:#6366f1">Change Text</button>
17 <button onclick="changeColor()" style="background:#0891b2">Change Color</button>
18 </div>
19
20 <div class="box">
21 <h3>💾 localStorage (Client-side Storage)</h3>
22 <input id="inp" placeholder="Type something..." style="padding:8px;border-radius:6px;border:none;background:#334155;color:white;margin-right:8px">
23 <button onclick="saveData()" style="background:#16a34a">Save to Browser</button>
24 <button onclick="loadData()" style="background:#9333ea">Load from Browser</button>
25 <div id="storage-display">Stored: (nothing yet)</div>
26 </div>
27
28 <script>
29 // DOM manipulation
30 const messages = ['Hello, Browser!', 'Running on YOUR device!', 'No server needed!', 'Pure client-side JS!'];
31 let msgIndex = 0;
32 function changeText() {
33 document.getElementById('output').textContent = messages[msgIndex++ % messages.length];
34 }
35 function changeColor() {
36 const colors = ['#4ade80','#60a5fa','#f472b6','#fb923c','#a78bfa'];
37 document.getElementById('output').style.color = colors[Math.floor(Math.random()*colors.length)];
38 }
39
40 // localStorage
41 function saveData() {
42 const value = document.getElementById('inp').value;
43 localStorage.setItem('myData', value);
44 document.getElementById('storage-display').textContent = `Saved: "${value}" (reload page — it persists!)`;
45 }
46 function loadData() {
47 const value = localStorage.getItem('myData') || '(nothing saved yet)';
48 document.getElementById('storage-display').textContent = `Loaded from storage: "${value}"`;
49 }
50 </script>
51</body>
52</html>

Server-side JavaScript

Server-side JavaScript

Server-side JavaScript runs on a remote computer (the server) — not in the browser. The most common runtime is Node.js, which uses the V8 engine but replaces browser APIs with server-focused APIs like file system access, networking, and process management.

What changed in 2009

Before Node.js (2009), if you wanted server-side code you needed Python, PHP, Ruby, Java, or C#. You had to know two completely different languages for frontend and backend. Node.js let JavaScript developers write the entire application in one language — client and server.

What server-side JavaScript can do:

Database Access

Read and write to databases (MongoDB, PostgreSQL, MySQL, Redis) directly — not possible in browsers.

File System

Read, write, delete files on the server's disk. Process uploads, generate reports, manage logs.

Authentication & Secrets

Store API keys, passwords, tokens safely. Run authentication logic without exposing secrets to users.

Email & Notifications

Send emails via SMTP, push notifications, SMS, webhooks — server-to-server communication.

Business Logic

Process payments, calculate pricing, enforce rules, validate data before saving — trusted execution.

Background Tasks

Run cron jobs, process queues, handle webhooks, and perform long operations independent of any browser.

javascript
1// Node.js — runs on the SERVER, not in a browser
2const fs = require('fs'); // File system — NOT available in browsers
3const http = require('http'); // HTTP server — NOT available in browsers
4
5// ✅ Read a file from disk
6const content = fs.readFileSync('./data/users.json', 'utf8');
7const users = JSON.parse(content);
8console.log(`Loaded ${users.length} users from disk`);
9
10// ✅ Create an HTTP server
11const server = http.createServer((req, res) => {
12 if (req.url === '/api/users' && req.method === 'GET') {
13 // Send JSON response to the browser
14 res.writeHead(200, { 'Content-Type': 'application/json' });
15 res.end(JSON.stringify(users));
16 } else {
17 res.writeHead(404);
18 res.end('Not found');
19 }
20});
21
22server.listen(3000, () => {
23 console.log('Server running at http://localhost:3000');
24});
25
26// ✅ Connect to a database (MongoDB example)
27const { MongoClient } = require('mongodb');
28async function getUsers() {
29 const client = new MongoClient('mongodb://localhost:27017');
30 await client.connect();
31 const db = client.db('myapp');
32 const users = await db.collection('users').find({}).toArray();
33 console.log(users);
34 await client.close();
35}
36getUsers();

Never expose secrets in client-side code

Everything in client-side JavaScript is visible to the user — they can open DevTools and read your code. Never put in browser code:

  • Database passwords or connection strings
  • Private API keys or secret tokens
  • Payment processing secret keys
  • Admin credentials

These belong on the server only. The browser should only receive public API keys (like Google Maps public key) that are designed to be exposed.

Side-by-Side Comparison

Side-by-Side Comparison

Client-side JavaScriptServer-side JavaScript
Where it runs: User's browser / device Runtime: V8, SpiderMonkey, JSCore (built into the browser) APIs available: DOM, BOM, Web APIs fetch, localStorage Canvas, WebGL, WebRTC Geolocation, Camera Can access: ✅ The webpage / DOM ✅ Browser storage ✅ User's device APIs ❌ Database directly ❌ Server file system ❌ Private API keys Security: Code is PUBLIC — user can read all of it Performance limit: User's device hardwareWhere it runs: Remote server (data center) Runtime: Node.js, Deno, Bun (standalone runtimes) APIs available: fs, http, crypto process, Buffer, Stream child_process, cluster OS-level networking Can access: ✅ Database directly ✅ File system ✅ Private secrets/keys ✅ Other servers ✅ Background tasks ❌ User's browser DOM ❌ User's local storage Security: Code is PRIVATE — user never sees it Performance limit: Server hardware (scalable)
FeatureClient-sideServer-side
Where it runsUser's browserRemote server
RuntimeBrowser (V8, SpiderMonkey)Node.js / Deno / Bun
DOM access✅ Full access❌ No DOM
Database access❌ Never directly✅ Full access
File system❌ Restricted✅ Full access
Code visibility❌ Fully visible to user✅ Hidden from user
Secrets/API keys❌ Never safe here✅ Safe here
localStorage✅ Read/write❌ Not available
HTTP requests✅ fetch(), XHR✅ http module, fetch
Runs without browser❌ Needs a browser✅ Runs standalone
Scales with users✅ Each user runs own JS⚠️ Must scale server
Performance costUser's CPU/RAMServer CPU/RAM

How Client and Server Work Together

How Client and Server Work Together

In a real web application, client-side and server-side JavaScript work together in a request-response cycle. The browser never talks directly to a database — it always goes through the server.

1

1. User opens the browser

The user navigates to https://myapp.com. The browser sends an HTTP GET request to the server.

2

2. Server sends HTML, CSS, JS files

The Node.js server responds with the HTML page and links to CSS and JavaScript files. The browser downloads and runs them.

3

3. Client-side JS runs in the browser

The downloaded JavaScript runs in V8 (in the browser). It renders the UI, sets up event listeners, and may show a loading state.

4

4. Client-side JS calls the API

The browser JS makes a fetch('/api/products') request to the server to load data. This is an HTTP request — not a direct database connection.

javascript
1// Client-side — running in the browser
2async function loadProducts() {
3 const response = await fetch('/api/products'); // HTTP request to server
4 const products = await response.json();
5 renderProducts(products); // Update the DOM
6}
7loadProducts();
5

5. Server-side JS handles the request

The Node.js server receives the request, queries the database, applies business logic, and sends back JSON data. The database password never leaves the server.

javascript
1// Server-side — running in Node.js (user never sees this)
2app.get('/api/products', async (req, res) => {
3 const products = await db.query(
4 'SELECT * FROM products WHERE active = true'
5 // Database credentials are safe on the server
6 );
7 res.json(products); // Send JSON to the browser
8});
6

6. Client renders the response

The browser JS receives the JSON, updates the DOM to display the products. The user sees the updated page — no full reload required.

The API Layer is the Bridge

The server exposes a REST API (or GraphQL API) that the client consumes. The client sends HTTP requests, the server responds with JSON. This clean separation means:

  • The client never knows about the database
  • The server never directly touches the user's browser
  • Either side can be rewritten independently
  • The same API can serve web, mobile, and desktop clients

Server-side Runtimes

Server-side JavaScript Runtimes

Multiple runtimes let you run JavaScript on a server. Each has different strengths, APIs, and design philosophies.

RuntimeEngineCreated ByYearKey Strength
Node.jsV8Ryan Dahl2009Largest ecosystem (npm). Battle-tested. Widest hosting support.
DenoV8Ryan Dahl2018Secure by default. TypeScript built-in. Web-standard APIs. No node_modules.
BunJavaScriptCoreJarred Sumner2022Extremely fast. Built-in bundler, test runner, package manager.
Cloudflare WorkersV8Cloudflare2017Runs at the edge (150+ locations). Near-zero cold start. Service Worker API.
AWS Lambda (Node)V8Amazon2014Serverless. Pay per invocation. Scales to zero automatically.

Frameworks for Each Side

Popular Frameworks for Each Side

Pro Tips
  • 1React
  • 2Vue
  • 3Angular
  • 4Svelte
  • 5Solid
  • 6Preact
Pro Tips
  • 1Express
  • 2Fastify
  • 3NestJS
  • 4Hono
  • 5Koa
  • 6AdonisJS
Pro Tips
  • 1Next.js
  • 2Nuxt
  • 3SvelteKit
  • 4Remix
  • 5Astro
  • 6SolidStart

Full-stack frameworks run code in both places

Frameworks like Next.js let you write client and server code in the same project, sometimes in the same file. A Next.js page.tsx can have a getServerSideProps function that runs on the server (safe to access the DB) and a React component that runs in the browser (updates the DOM). The framework handles the boundary between the two.

Same Task — Client vs Server

Same Task — Two Different Approaches

Let's see the same goal achieved on the client side vs the server side to understand their differences clearly.

✗ BadClient-side — Fetch & Display
// Runs in the BROWSER
// Gets data from the server, updates DOM
async function loadUserProfile(userId) {
// Can't query DB directly from browser
// Must go through the server API
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) throw new Error('User not found');
const user = await res.json();
// Update the DOM with received data
document.getElementById('name').textContent = user.name;
document.getElementById('email').textContent = user.email;
document.getElementById('avatar').src = user.avatarUrl;
}
// Called when the page loads
loadUserProfile(42);
✓ GoodServer-side — Query & Respond
// Runs in NODE.JS on the SERVER
// Queries DB directly, sends response
const express = require('express');
const db = require('./database'); // private DB connection
const app = express();
app.get('/api/users/:id', async (req, res) => {
const { id } = req.params;
// Can query database directly — safe here
const user = await db.query(
'SELECT id, name, email, avatar_url FROM users WHERE id = $1',
[id] // parameterized — prevents SQL injection
);
if (!user) {
return res.status(404).json({ error: 'Not found' });
}
// Send data to browser as JSON
res.json(user);
});
app.listen(3000);
✗ BadClient-side Form Validation
// Runs in the BROWSER
// Instant feedback — no server round-trip needed
const form = document.querySelector('#signup-form');
form.addEventListener('submit', (e) => {
e.preventDefault();
const email = form.email.value.trim();
const password = form.password.value;
// Instant client-side validation
if (!email.includes('@')) {
showError('Invalid email address');
return;
}
if (password.length < 8) {
showError('Password must be 8+ characters');
return;
}
// All good — send to server
submitToServer({ email, password });
});
✓ GoodServer-side Form Validation
// Runs in NODE.JS on the SERVER
// ALWAYS validate on server — client can be bypassed!
app.post('/api/signup', async (req, res) => {
const { email, password } = req.body;
// Server MUST re-validate — client code can be
// disabled or tampered with by any user
if (!email || !email.includes('@')) {
return res.status(400).json({ error: 'Invalid email' });
}
if (!password || password.length < 8) {
return res.status(400).json({ error: 'Password too short' });
}
// Check if email already exists (requires DB — only possible here)
const existing = await db.findUserByEmail(email);
if (existing) {
return res.status(409).json({ error: 'Email already in use' });
}
// Hash password before storing (bcrypt — never store plain text)
const hash = await bcrypt.hash(password, 12);
await db.createUser({ email, passwordHash: hash });
res.status(201).json({ message: 'Account created' });
});

Never rely only on client-side validation

Client-side validation improves UX (instant feedback, no round-trip), but it can be completely bypassed — a user can open DevTools, disable JavaScript, or send raw HTTP requests with invalid data. Always validate on the server too. Client validation = good UX. Server validation = actual security.

Rendering Strategies

Rendering Strategies — Where HTML is Generated

A key decision in modern web development is where the HTML for a page is generated — on the server, in the browser, or both. This decision affects performance, SEO, and user experience.

StrategyWhere HTML is BuiltFirst LoadSEOUse Case
CSR — Client-Side RenderingBrowser (JS builds the DOM)Slow (blank page first)Poor (bots see empty page)Dashboards, apps behind login
SSR — Server-Side RenderingServer (on every request)Fast (full HTML sent)Great (full HTML for bots)News sites, e-commerce, blogs
SSG — Static Site GenerationServer (at build time)Fastest (pre-built HTML)GreatMarketing pages, documentation
ISR — Incremental Static RegenerationServer (on demand, then cached)FastGreatE-commerce with frequent updates
Streaming SSRServer (sent in chunks)Very fast (progressive)GreatLarge pages with slow data fetches

Test Your Knowledge

Test Your Knowledge

Quick Check

Which of these tasks MUST be done on the server side?

Quick Check

A developer puts their database password in client-side JavaScript. What is the risk?

Quick Check

What is the main role of Node.js?

Quick Check

In a typical web app, how does client-side JavaScript get data from a database?

Quick Check

What does 'CSR' stand for and what is its main drawback?

Key Takeaways

Key Takeaways

Pro Tips
  • 1Client-side JS runs in the browser on the user's device. Server-side JS runs on a remote server. Same language, completely different environments.
  • 2Everything in client-side code is visible to the user — never put secrets, passwords, or private keys in browser JavaScript.
  • 3Always validate user input on the server, even if you also validate on the client. Client-side validation can be bypassed.
  • 4Browsers cannot connect to databases directly. Always use a server API as the middleman.
  • 5Node.js, Deno, and Bun let JavaScript run on servers. Node.js has the largest ecosystem and is the most widely deployed.
  • 6Full-stack frameworks (Next.js, Nuxt, SvelteKit) let you write client and server code in one project, sometimes in the same file.
  • 7Choose your rendering strategy (CSR, SSR, SSG) based on your SEO needs, content update frequency, and performance requirements.
  • 8JavaScript being usable on both sides is unique — every other popular language requires learning a separate frontend language (HTML/JS) anyway.
What's Next?

JavaScript Engine Overview

Understand how the JS engine parses, compiles, and runs your code under the hood.

Variables & Data Types

Start writing JavaScript — the fundamentals that work on both client and server.

fetch() & APIs

Learn how client-side JS communicates with servers using the Fetch API.

Node.js Basics

Get started with server-side JavaScript using Node.js and Express.

You now understand where JavaScript runs!

Client-side handles what users see and interact with. Server-side handles data, security, and business logic. Together they form the full-stack web — and JavaScript is the only language that spans both sides natively.

Try it in the Javascript Compiler

Run and experiment with Javascript code right in your browser — no setup needed.

Continue Learning