Skip to main content
The Vault API provides a comprehensive interface for working with files and folders in Obsidian. This guide covers the essential operations for file management.

Vault Overview

The Vault class is accessible via this.app.vault and provides methods for all file operations:
import { Plugin, TFile, TFolder } from 'obsidian';

export default class FilePlugin extends Plugin {
  async onload() {
    const vault = this.app.vault;
    // Access vault methods here
  }
}

Reading Files

Read Text Files

Use read() for files you intend to modify, or cachedRead() for read-only access:
// Read file for modification
const file = this.app.vault.getAbstractFileByPath('note.md') as TFile;
if (file) {
  const content = await this.app.vault.read(file);
  console.log(content);
}

// Cached read (faster, for display only)
const cachedContent = await this.app.vault.cachedRead(file);
Use read() if you plan to modify the file content. Use cachedRead() for better performance when only displaying content.

Read Binary Files

For images, PDFs, and other binary files:
const imageFile = this.app.vault.getAbstractFileByPath('image.png') as TFile;
if (imageFile) {
  const arrayBuffer = await this.app.vault.readBinary(imageFile);
  // Process binary data
}

Writing Files

Create New Files

Create plaintext or binary files:
// Create text file
const newFile = await this.app.vault.create(
  'folder/new-note.md',
  '# New Note\n\nContent here'
);

// Create binary file
const imageBuffer = new ArrayBuffer(1024);
const binaryFile = await this.app.vault.createBinary(
  'folder/image.png',
  imageBuffer
);
Paths should be normalized using normalizePath() before passing to vault methods.

Modify Existing Files

Update file content:
const file = this.app.vault.getAbstractFileByPath('note.md') as TFile;
if (file) {
  await this.app.vault.modify(file, 'Updated content');
}

// Modify binary file
await this.app.vault.modifyBinary(file, arrayBuffer);

Append to Files

Add content to the end of a file:
// Append text
await this.app.vault.append(file, '\n\nNew paragraph');

// Append binary data
await this.app.vault.appendBinary(file, dataBuffer);

Atomic Operations

Process File Content

Atomically read, modify, and save:
const result = await this.app.vault.process(file, (content) => {
  // Modify content
  return content.replace('old', 'new');
});

console.log('Saved content:', result);
The process() method ensures atomic updates - the file is read, modified, and written as a single operation.

File and Folder Management

Get Files and Folders

// Get file by path
const file = this.app.vault.getFileByPath('note.md');

// Get folder by path
const folder = this.app.vault.getFolderByPath('folder');

// Get either file or folder
const abstractFile = this.app.vault.getAbstractFileByPath('path');

// Check type
if (abstractFile instanceof TFile) {
  console.log('File:', abstractFile.basename);
} else if (abstractFile instanceof TFolder) {
  console.log('Folder:', abstractFile.name);
}

Get All Files

// Get all markdown files
const markdownFiles = this.app.vault.getMarkdownFiles();

// Get all files
const allFiles = this.app.vault.getFiles();

// Get all folders
const folders = this.app.vault.getAllFolders();

// Get all files and folders
const everything = this.app.vault.getAllLoadedFiles();

Create Folders

try {
  const folder = await this.app.vault.createFolder('new-folder');
  console.log('Created folder:', folder.path);
} catch (error) {
  console.error('Folder already exists');
}

File Operations

Rename Files

For proper link updating, use FileManager.renameFile():
// Using FileManager (recommended - updates links)
await this.app.fileManager.renameFile(file, 'new-name.md');

// Using Vault (no link updates)
await this.app.vault.rename(file, 'new-path/new-name.md');

Copy Files

const copiedFile = await this.app.vault.copy(file, 'backup/note-copy.md');

Delete Files

// Delete permanently
await this.app.vault.delete(file);

// Move to trash (respects user settings)
await this.app.vault.trash(file, true);

// Or use FileManager for confirmation prompt
const confirmed = await this.app.fileManager.promptForDeletion(file);
if (confirmed) {
  await this.app.fileManager.trashFile(file);
}

TFile and TFolder Classes

TFile Properties

const file: TFile;
console.log(file.path);      // "folder/note.md"
console.log(file.name);       // "note.md"
console.log(file.basename);   // "note"
console.log(file.extension);  // "md"
console.log(file.parent);     // TFolder instance
console.log(file.stat.size);  // File size in bytes
console.log(file.stat.ctime); // Creation time (ms)
console.log(file.stat.mtime); // Modification time (ms)

TFolder Properties

const folder: TFolder;
console.log(folder.path);     // "folder"
console.log(folder.name);     // "folder"
console.log(folder.children); // Array of TFile and TFolder
console.log(folder.isRoot()); // boolean

Vault Events

Listen to File Changes

onload() {
  // File created
  this.registerEvent(
    this.app.vault.on('create', (file) => {
      console.log('Created:', file.path);
    })
  );

  // File modified
  this.registerEvent(
    this.app.vault.on('modify', (file) => {
      console.log('Modified:', file.path);
    })
  );

  // File deleted
  this.registerEvent(
    this.app.vault.on('delete', (file) => {
      console.log('Deleted:', file.path);
    })
  );

  // File renamed
  this.registerEvent(
    this.app.vault.on('rename', (file, oldPath) => {
      console.log(`Renamed: ${oldPath} -> ${file.path}`);
    })
  );
}
Create events are fired when the vault first loads. To avoid processing existing files, register handlers inside Workspace.onLayoutReady.

Working with Frontmatter

Process Frontmatter

Use FileManager.processFrontMatter() for safe YAML updates:
await this.app.fileManager.processFrontMatter(file, (frontmatter) => {
  frontmatter['tags'] = ['new-tag'];
  frontmatter['modified'] = new Date().toISOString();
  delete frontmatter['old-key'];
});

Get Frontmatter Info

import { getFrontMatterInfo } from 'obsidian';

const content = await this.app.vault.read(file);
const fmInfo = getFrontMatterInfo(content);

console.log(fmInfo.exists);        // Has frontmatter?
console.log(fmInfo.frontmatter);   // Frontmatter string
console.log(fmInfo.from);          // Start offset
console.log(fmInfo.to);            // End offset
console.log(fmInfo.contentStart);  // Where content begins

Best Practices

1
Use Normalized Paths
2
Always normalize paths before using them:
3
import { normalizePath } from 'obsidian';

const path = normalizePath('folder//file.md'); // "folder/file.md"
const file = this.app.vault.getFileByPath(path);
4
Handle Errors Gracefully
5
try {
  await this.app.vault.create('note.md', 'content');
} catch (error) {
  if (error.message.includes('already exists')) {
    console.log('File exists, using existing file');
  } else {
    throw error;
  }
}
6
Use FileManager for User-Facing Operations
7
FileManager provides higher-level operations with proper link updates:
8
// Generate proper markdown links
const link = this.app.fileManager.generateMarkdownLink(
  file,
  'source.md',
  '#heading',
  'Display Text'
);

// Get proper attachment path
const attachmentPath = await this.app.fileManager.getAvailablePathForAttachment(
  'image.png',
  'source.md'
);

Creating Views

Build custom views for your files

Editor Integration

Work with the editor API

Build docs developers (and LLMs) love