π¦ The fs Module
fs (File System) is a built-in Node.js module for reading, writing, and managing files and folders. Every method below comes in two flavors β sync and async β depending on whether you want your code to wait or keep moving.
π Synchronous (Blocking)
Sync methods pause your code until the file operation finishes. Nothing else runs until the write or read completes β simple to reason about, but risky in apps that need to stay responsive.
const fs = require('fs');
// Write
fs.writeFileSync('note.txt', 'This is a sync write.');
console.log('β
File written successfully!');
// Read
const data = fs.readFileSync('note.txt', 'utf8');
console.log('π File content:', data);writeFileSync(path, content)creates the file if it doesn't exist, or overwrites it if it does.readFileSync(path, 'utf8')returns the file's content as a plain string. Skip'utf8'and you'd get raw binary data instead.Because these are sync,
console.logonly runs after the operation finishes β no waiting or callback needed.
β³ Asynchronous (Non-blocking)
Async methods use a callback (or promises) so your app doesn't freeze while the disk does its work. This matters most in servers, where blocking would stall every other request.
const fs = require('fs');
// Write
fs.writeFile('async.txt', 'This is an async write.', (err) => {
if (err) throw err;
console.log('β
Async file written!');
});
// Read
fs.readFile('async.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('π Async file content:', data);
});writeFileandreadFilereturn immediately β the rest of your script keeps running while the file operation happens in the background.The callback
(err, data)fires once the operation finishes.errholds an error if something went wrong; check it before usingdata.Since these don't block, the order things print to the console can differ from the order you wrote the code in.
β Appending to Files
Adds content to the end of a file without wiping out what's already there β unlike writeFileSync, which overwrites everything.
fs.appendFileSync('note.txt', '\nThis is appended.');
console.log('π Content appended!');File after append:
This is a sync write.
This is appended.The
\nadds a line break first, so the new text starts on its own line instead of running into the existing content.If the file doesn't exist yet,
appendFileSynccreates it β same aswriteFileSync.
ποΈ Deleting Files
fs.unlinkSync('note.txt');
console.log('ποΈ File deleted!');β οΈ unlinkSync removes the file permanently β there's no trash or undo. If the file doesn't exist, this throws an error and crashes your script unless you handle it.
β Delete Safely
const fs = require('fs');
const filePath = 'note.txt';
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
console.log('ποΈ File deleted!');
} else {
console.log('β File not found.');
}existsSync(path)checks whether the file is there first, and returnstrueorfalse.Wrapping the delete in that check means your code handles a missing file gracefully instead of throwing an error.
π Working with Folders
Starting point β just the project file itself, no myFolder yet:
project/
βββ app.jsStep 1 β Create a folder:
fs.mkdirSync('myFolder');
console.log('π Folder created!');mkdirSync makes a new, empty folder at the given path. It throws an error if a folder with that name already exists.
project/
βββ app.js
βββ myFolder/ β new, emptyStep 2 β Create a file inside it:
fs.writeFileSync('myFolder/info.txt', 'Hello from inside the folder!');Same writeFileSync as before β just pointed at a path inside the folder you just made.
project/
βββ app.js
βββ myFolder/
βββ info.txt β newStep 3 β Clean up: delete the file, then the folder:
fs.unlinkSync('myFolder/info.txt'); // delete file first
fs.rmdirSync('myFolder'); // then delete the folder
console.log('π§Ή Folder cleaned up!');Folders must be empty before
rmdirSynccan remove them β that's why the file inside gets deleted first.Trying to delete the folder while it still has files in it will throw an error.
project/
βββ app.js β back to the starting point<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Working with folders</title>
</head>
<body>
<div class="fd-info" id="fdinfo">
<style>
.fd-info {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-deep: #1942a8;
--accent-soft: #e3ebfd;
--line: #ddd6c9;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
--mono-font: 'IBM Plex Mono', monospace;
max-width: 880px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
color: var(--ink);
font-family: var(--body-font);
}
.fd-info[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #13171f;
--paper-alt: #1c212b;
--accent: #5b8def;
--accent-deep: #8fb4ff;
--accent-soft: #20293f;
--line: #2c313d;
}
.fd-info * { box-sizing: border-box; min-width: 0; }
.fd-eyebrow {
font-family: var(--mono-font); font-size: 0.72rem; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--accent); margin: 0 0 0.45rem;
display: flex; align-items: center; justify-content: center; gap: 0.4rem;
}
.fd-eyebrow::before { content: ""; width: 14px; height: 2px; background: var(--accent); display: inline-block; }
.fd-title {
font-family: var(--display-font); font-size: clamp(1.5rem, 4vw, 2.1rem); text-align: center;
margin: 0 0 0.5rem; line-height: 1.16; letter-spacing: -0.01em;
}
.fd-sub { color: var(--ink-soft); font-size: 0.92rem; text-align: center; margin: 0 0 1.6rem; }
.fd-window { border-radius: 8px; overflow: hidden; box-shadow: 0 16px 40px -14px rgba(0,0,0,0.45); border: 1px solid #1e1e1e; font-family: var(--mono-font); }
.fd-titlebar { background: #323233; display: flex; align-items: center; padding: 0.5rem 0.8rem; position: relative; }
.fd-dots { display: flex; gap: 0.4rem; }
.fd-dots span { width: 10px; height: 10px; border-radius: 50%; display: block; }
.fd-dots span:nth-child(1) { background: #ff5f56; }
.fd-dots span:nth-child(2) { background: #ffbd2e; }
.fd-dots span:nth-child(3) { background: #27c93f; }
.fd-titlebar-text { position: absolute; left: 50%; transform: translateX(-50%); font-size: 0.72rem; color: #cccccc; }
.fd-body { display: flex; height: 300px; }
.fd-sidebar { width: 180px; background: #252526; flex-shrink: 0; overflow-y: auto; border-right: 1px solid #1e1e1e; padding-top: 0.3rem; }
.fd-sidebar-head { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.06em; color: #bbbbbb; padding: 0.5rem 0.8rem 0.3rem; }
.fd-sidebar-root { font-size: 0.74rem; color: #cccccc; padding: 0.2rem 0.7rem; display: flex; align-items: center; gap: 0.3rem; }
.fd-tree-row { display: flex; align-items: center; gap: 0.35rem; padding: 0.22rem 0.7rem 0.22rem 1.4rem; font-size: 0.74rem; color: #cccccc; }
.fd-tree-row.is-nested { padding-left: 2.4rem; }
.fd-tree-row.is-appearing { animation: fd-row-in 0.35s ease; }
.fd-tree-row.is-leaving { animation: fd-row-out 0.3s ease forwards; }
@keyframes fd-row-in { from { opacity: 0; transform: translateX(-6px); background: rgba(97,175,239,0.25); } to { opacity: 1; transform: none; } }
@keyframes fd-row-out { from { opacity: 1; } to { opacity: 0; transform: translateX(-6px); } }
.fd-badge { width: 15px; height: 15px; border-radius: 3px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 0.46rem; font-weight: 700; color: #1a1a1a; background: #f0db4f; }
.fd-folder-icon { width: 13px; height: 13px; flex-shrink: 0; color: #dcb67a; }
.fd-txt-badge { width: 15px; height: 15px; border-radius: 3px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 0.42rem; font-weight: 700; color: #cccccc; background: #3c3c3c; }
.fd-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.fd-tabbar { display: flex; background: #252526; border-bottom: 1px solid #1e1e1e; }
.fd-tab { display: flex; align-items: center; gap: 0.35rem; background: #1e1e1e; color: #d4d4d4; font: inherit; font-size: 0.72rem; padding: 0.45rem 0.8rem; border: none; border-right: 1px solid #1e1e1e; border-top: 2px solid #0078d4; }
.fd-code-wrap { flex: 1; background: #1e1e1e; overflow: auto; padding: 0.7rem 0; }
.fd-code-line { display: flex; gap: 0.9rem; padding: 0.06rem 1rem; font-size: 0.73rem; line-height: 1.6; white-space: pre; transition: background 0.25s; }
.fd-code-line.is-current { background: rgba(97, 175, 239, 0.16); }
.fd-ln { color: #5a5a5a; user-select: none; width: 16px; text-align: right; flex-shrink: 0; }
.fd-code-line.is-current .fd-ln { color: #61afef; }
.fd-src { color: #d4d4d8; }
.tok-kw { color: #c678dd; }
.tok-fn { color: #61afef; }
.tok-str { color: #98c379; }
.tok-punc { color: #6b7280; }
.tok-comment { color: #6a9955; font-style: italic; }
.fd-terminal { background: #181818; border-top: 1px solid #2c2c2c; display: flex; flex-direction: column; height: 110px; flex-shrink: 0; }
.fd-term-head { font-size: 0.62rem; letter-spacing: 0.05em; color: #9a9a9a; padding: 0.35rem 0.8rem; border-bottom: 1px solid #262626; text-transform: uppercase; }
.fd-term-body { flex: 1; overflow-y: auto; padding: 0.45rem 0.9rem; font-size: 0.74rem; line-height: 1.6; }
.fd-term-line { color: #d4d4d4; white-space: pre; opacity: 0; transform: translateY(2px); animation: fd-in 0.2s ease forwards; }
@keyframes fd-in { to { opacity: 1; transform: none; } }
.fd-term-line.is-cmd { color: #7ee787; }
.fd-caption { text-align: center; font-size: 0.86rem; color: var(--ink-soft); line-height: 1.5; min-height: 2.6em; margin: 1.1rem 0 1rem; }
.fd-caption b { color: var(--ink); }
.fd-controls { display: flex; justify-content: center; align-items: center; gap: 0.7rem; margin-bottom: 0.5rem; }
.fd-btn {
font-family: var(--mono-font); font-size: 0.82rem; font-weight: 600; padding: 0.55rem 1.1rem;
border-radius: 5px; border: 1px solid var(--accent); background: var(--accent); color: #fff; cursor: pointer;
}
.fd-btn.is-ghost { background: transparent; color: var(--ink-soft); border-color: var(--line); }
.fd-btn:disabled { opacity: 0.4; cursor: default; }
.fd-step-count { font-family: var(--mono-font); font-size: 0.74rem; color: var(--ink-soft); }
.fd-hint { text-align: center; font-size: 0.8rem; color: var(--ink-soft); margin: 0.9rem 0 0; padding-bottom: 1.5rem; }
@media (prefers-reduced-motion: reduce) {
.fd-info *, .fd-info *::before, .fd-info *::after { transition-duration: 0.001ms !important; animation-duration: 0.001ms !important; }
}
</style>
<div class="fd-window">
<div class="fd-titlebar">
<div class="fd-dots"><span></span><span></span><span></span></div>
<div class="fd-titlebar-text">project β Visual Studio Code</div>
</div>
<div class="fd-body">
<div class="fd-sidebar">
<div class="fd-sidebar-head">EXPLORER</div>
<div class="fd-sidebar-root">βΎ PROJECT</div>
<div class="fd-tree-row"><span class="fd-badge">JS</span><span>app.js</span></div>
<div id="fdTreeExtra"></div>
</div>
<div class="fd-main">
<div class="fd-tabbar">
<div class="fd-tab"><span class="fd-badge">JS</span><span>app.js</span></div>
</div>
<div class="fd-code-wrap" id="fdCode">
<div class="fd-code-line" data-line="1"><span class="fd-ln">1</span><span class="fd-src"><span class="tok-kw">const</span> fs = <span class="tok-fn">require</span><span class="tok-punc">(</span><span class="tok-str">'fs'</span><span class="tok-punc">);</span></span></div>
<div class="fd-code-line" data-line="2"><span class="fd-ln">2</span><span class="fd-src"> </span></div>
<div class="fd-code-line" data-line="3"><span class="fd-ln">3</span><span class="fd-src"><span class="tok-comment">// Step 1: create a folder</span></span></div>
<div class="fd-code-line" data-line="4"><span class="fd-ln">4</span><span class="fd-src">fs.<span class="tok-fn">mkdirSync</span><span class="tok-punc">(</span><span class="tok-str">'myFolder'</span><span class="tok-punc">);</span></span></div>
<div class="fd-code-line" data-line="5"><span class="fd-ln">5</span><span class="fd-src">console.<span class="tok-fn">log</span><span class="tok-punc">(</span><span class="tok-str">'π Folder created!'</span><span class="tok-punc">);</span></span></div>
<div class="fd-code-line" data-line="6"><span class="fd-ln">6</span><span class="fd-src"> </span></div>
<div class="fd-code-line" data-line="7"><span class="fd-ln">7</span><span class="fd-src"><span class="tok-comment">// Step 2: create a file inside it</span></span></div>
<div class="fd-code-line" data-line="8"><span class="fd-ln">8</span><span class="fd-src">fs.<span class="tok-fn">writeFileSync</span><span class="tok-punc">(</span><span class="tok-str">'myFolder/info.txt'</span><span class="tok-punc">,</span> <span class="tok-str">'Hello from inside the folder!'</span><span class="tok-punc">);</span></span></div>
<div class="fd-code-line" data-line="9"><span class="fd-ln">9</span><span class="fd-src"> </span></div>
<div class="fd-code-line" data-line="10"><span class="fd-ln">10</span><span class="fd-src"><span class="tok-comment">// Step 3: clean up</span></span></div>
<div class="fd-code-line" data-line="11"><span class="fd-ln">11</span><span class="fd-src">fs.<span class="tok-fn">unlinkSync</span><span class="tok-punc">(</span><span class="tok-str">'myFolder/info.txt'</span><span class="tok-punc">);</span> <span class="tok-comment">// delete file first</span></span></div>
<div class="fd-code-line" data-line="12"><span class="fd-ln">12</span><span class="fd-src">fs.<span class="tok-fn">rmdirSync</span><span class="tok-punc">(</span><span class="tok-str">'myFolder'</span><span class="tok-punc">);</span> <span class="tok-comment">// then delete the folder</span></span></div>
<div class="fd-code-line" data-line="13"><span class="fd-ln">13</span><span class="fd-src">console.<span class="tok-fn">log</span><span class="tok-punc">(</span><span class="tok-str">'π§Ή Folder cleaned up!'</span><span class="tok-punc">);</span></span></div>
</div>
<div class="fd-terminal">
<div class="fd-term-head">Terminal</div>
<div class="fd-term-body" id="fdTerm"><div class="fd-term-line is-cmd" style="animation-delay:0s;">$ node app.js</div></div>
</div>
</div>
</div>
</div>
<p class="fd-caption" id="fdCaption">Press Next to run the first line.</p>
<div class="fd-controls">
<button type="button" class="fd-btn is-ghost" id="fdResetBtn">↻ Restart</button>
<button type="button" class="fd-btn" id="fdNextBtn">Next step →</button>
</div>
<div class="fd-controls">
<span class="fd-step-count" id="fdStepCount">Step 0 / 6</span>
</div>
<p class="fd-hint">Folders must be empty before rmdirSync can remove them — that’s why the file gets deleted first.</p>
</div>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@500;600&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<script>
(function () {
var root = document.getElementById('fdinfo');
if (!root) return;
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') dark = true;
else if (bodyAttr === 'false') dark = false;
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) root.setAttribute('data-theme', 'dark');
var codeLines = root.querySelectorAll('.fd-code-line');
var treeExtra = root.querySelector('#fdTreeExtra');
var term = root.querySelector('#fdTerm');
var caption = root.querySelector('#fdCaption');
var nextBtn = root.querySelector('#fdNextBtn');
var resetBtn = root.querySelector('#fdResetBtn');
var stepCount = root.querySelector('#fdStepCount');
var FOLDER_SVG = '<svg class="fd-folder-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg>';
function highlightLine(n) {
codeLines.forEach(function (l) {
l.classList.toggle('is-current', l.getAttribute('data-line') === String(n));
});
}
function addTerm(text) {
var div = document.createElement('div');
div.className = 'fd-term-line';
div.textContent = text;
term.appendChild(div);
term.scrollTop = term.scrollHeight;
}
function addFolderRow() {
var row = document.createElement('div');
row.id = 'fdFolderRow';
row.className = 'fd-tree-row is-appearing';
row.innerHTML = FOLDER_SVG + '<span>myFolder</span>';
treeExtra.appendChild(row);
}
function addFileRow() {
var row = document.createElement('div');
row.id = 'fdFileRow';
row.className = 'fd-tree-row is-nested is-appearing';
row.innerHTML = '<span class="fd-txt-badge">TXT</span><span>info.txt</span>';
treeExtra.appendChild(row);
}
function removeRow(id) {
var row = document.getElementById(id);
if (!row) return;
row.classList.add('is-leaving');
setTimeout(function () { if (row.parentNode) row.parentNode.removeChild(row); }, 280);
}
var steps = [
{ line: 4, caption: '<b>mkdirSync</b> makes a new, empty folder. It throws if one with that name already exists.', action: function () { addFolderRow(); } },
{ line: 5, caption: 'Confirms it happened.', action: function () { addTerm('\uD83D\uDCC1 Folder created!'); } },
{ line: 8, caption: 'Same <b>writeFileSync</b> as always — just pointed at a path inside the folder.', action: function () { addFileRow(); } },
{ line: 11, caption: 'Deletes the file <b>first</b> — a folder can\u2019t be removed while anything is still inside it.', action: function () { removeRow('fdFileRow'); } },
{ line: 12, caption: 'Now that it\u2019s empty, <b>rmdirSync</b> can remove the folder itself.', action: function () { removeRow('fdFolderRow'); } },
{ line: 13, caption: 'Back to just app.js \u2014 exactly where we started.', action: function () { addTerm('\uD83E\uDDF9 Folder cleaned up!'); } }
];
var stepIndex = 0;
var total = steps.length;
function reset() {
stepIndex = 0;
codeLines.forEach(function (l) { l.classList.remove('is-current'); });
treeExtra.innerHTML = '';
term.innerHTML = '<div class="fd-term-line is-cmd" style="animation-delay:0s;">$ node app.js</div>';
caption.innerHTML = 'Press Next to run the first line.';
stepCount.textContent = 'Step 0 / ' + total;
nextBtn.disabled = false;
nextBtn.textContent = 'Next step \u2192';
}
nextBtn.addEventListener('click', function () {
if (stepIndex >= total) return;
var s = steps[stepIndex];
highlightLine(s.line);
caption.innerHTML = s.caption;
if (s.action) s.action();
stepIndex++;
stepCount.textContent = 'Step ' + stepIndex + ' / ' + total;
if (stepIndex >= total) {
nextBtn.disabled = true;
nextBtn.textContent = 'Done';
highlightLine(null);
}
});
resetBtn.addEventListener('click', reset);
reset();
})();
</script>
</body>
</html>