fs.mkdir()

Creates a directory asynchronously, optionally creating every missing parent along the way.

Signature
fs.mkdir(path[, options], callback)
  • Added in Node.js v0.1.8

Parameters

ParameterTypeDescription
$path<string> | <Buffer> | <URL>The directory to create.
$optionsoptional<Object> | <integer>= { recursive: false, mode: 0o777 }An integer is treated as the mode. As an object: recursive <boolean> (default false) to create missing parents and to succeed when the directory already exists, and mode <string>|<integer> (default 0o777, not supported on Windows).
$callback<Function>Called as (err, path). The path argument is present only when recursive is true and a directory was actually created; otherwise it is undefined.

Return value

undefinedNothing. With recursive: true the callback's second argument is the first directory that was created, or undefined if nothing needed creating.

Errors

Reports EEXIST when the directory already exists and recursive is false, ENOENT when a parent is missing and recursive is false, ENOTDIR when a path component is a file, and EACCES on permission failures, all through the callback.

Changelog

  • PHP v18.0.0Passing an invalid callback now throws ERR_INVALID_ARG_TYPE instead of ERR_INVALID_CALLBACK.
  • PHP v13.11.0, v12.17.0In recursive mode the callback now receives the first created path as its second argument.
  • PHP v10.12.0The second argument may be an options object with recursive and mode properties; before this, recursive creation had to be written by hand.
  • PHP v10.0.0The callback parameter is no longer optional; omitting it throws a TypeError at runtime.
  • PHP v7.6.0path may be a WHATWG URL object using the file: protocol.

fs.mkdir() creates one directory. With { recursive: true } it becomes the equivalent of mkdir -p: every missing parent is created, and an already-existing directory is not an error. That combination is what you want almost every time you are preparing an output directory.

recursive changes the error behaviour, not just the depth

Without it, calling twice fails with EEXIST, and creating a/b/c when a does not exist fails with ENOENT. With it, both cases succeed silently. This is why recursive: true makes the “check whether it exists, then create it” dance unnecessary — and safer, since that check has a race window.

The callback's second argument

In recursive mode you also receive the first directory that had to be created — creating build/static/img from scratch gives you 'build'. When nothing needed creating it is undefined, which is a neat way to tell “I made this” from “it was already here” without an extra stat.

One caveat: recursive: true also swallows the case where the path already exists as a directory but you assumed you owned it. And mode is masked by the umask, so 0o777 typically lands as 0o755.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Create a nested build output directory before writing generated files into it.
  • Prepare a per-date log or upload folder on first use without checking whether it exists.
  • Set up a temporary working directory at the start of a test and remove it afterwards.

Frequently asked questions

How do I do mkdir -p in Node.js?

Pass { recursive: true }: fs.mkdir('a/b/c', { recursive: true }, cb). It creates every missing parent and succeeds silently if the directory already exists, available since Node 10.12.0.

Why does fs.mkdir fail with EEXIST?

Because the directory is already there and recursive is false, which is the default. Add { recursive: true } instead of checking with existsSync first &mdash; it is both shorter and free of the check-then-act race.

How do I know whether the directory was created or already existed?

In recursive mode the callback's second argument is the first path that was actually created, and undefined when nothing needed creating. Compare against undefined rather than making an extra fs.stat() call.

More examples

Without recursive, a second call is an error

The second call reports EEXIST. Any code that creates a directory on every run and does not pass recursive: true has to catch and ignore that code explicitly, which is exactly the boilerplate recursive removes.

Example · javascript
const fs = require('node:fs');

fs.mkdir('once-only', (err) => {
  if (err) throw err;
  console.log('created');

  fs.mkdir('once-only', (err2) => {
    console.log('again ->', err2 && err2.code);
    fs.rmSync('once-only', { recursive: true, force: true });
  });
});

Missing parents need recursive too

The first attempt fails with ENOENT because 'nested' and 'nested/deep' do not exist &mdash; the plain form creates only the final component. The recursive attempt creates all three levels and reports a null error.

Example · javascript
const fs = require('node:fs');

fs.mkdir('nested/deep/tree', (err) => {
  console.log('without recursive:', err && err.code);

  fs.mkdir('nested/deep/tree', { recursive: true }, (err2) => {
    console.log('with recursive:', err2);
    console.log('exists:', fs.existsSync('nested/deep/tree'));
    fs.rmSync('nested', { recursive: true, force: true });
  });
});

Preparing an output directory before writing

This is the standard order of operations: create the directory, then write into it. Skipping the mkdir would make the writeFile fail with ENOENT, since writing never creates folders.

Example · javascript
import fs from 'node:fs';
import fsp from 'node:fs/promises';
import path from 'node:path';

async function main() {
  const outDir = path.join('dist', 'reports');
  await fsp.mkdir(outDir, { recursive: true });
  await fsp.writeFile(path.join(outDir, 'summary.txt'), 'all green\n');

  console.log('files:', await fsp.readdir(outDir));
  console.log('content:', (await fsp.readFile(path.join(outDir, 'summary.txt'), 'utf8')).trim());

  await fsp.rm('dist', { recursive: true, force: true });
  console.log('cleaned:', fs.existsSync('dist'));
}

main();

Discussion

  • Be the first to comment on this lesson.