diff --git a/frontend/fakenoteprovider.js b/frontend/fakenoteprovider.js
new file mode 100644
index 0000000..19e14e3
--- /dev/null
+++ b/frontend/fakenoteprovider.js
@@ -0,0 +1,86 @@
+import { NoteProvider } from "./noteprovider";
+
+class Note {
+    name;
+    content;
+    tags;
+
+    constructor(name) {
+        this.name = name;
+        this.content = "";
+        this.tags = [];
+    }
+}
+
+class FakeNoteProvider extends NoteProvider {
+
+    #notes;
+
+    constructor() {
+        super();
+        this.#notes = new Map([
+            ["Sample", new Note("Sample")]
+        ]);
+    }
+
+    #get_note(name) {
+        const note = this.#notes.get(name);
+        if (!note) { throw new Error(`unknown note \"${name}\"`); }
+        return note;
+    }
+
+    async list() {
+        return [...this.#notes.keys()];
+    }
+
+    async read(name) {
+        const note = this.#get_note(name);
+        return note.name;
+    }
+
+    async create() {
+        let name = "Untitled";
+        let count = 0;
+        while (this.#notes.has(name)) {
+            count++;
+            name = `Untitled ${count}`;
+        }
+
+        this.#notes.set(name, new Note(name));
+        return name;
+    }
+
+    async rename(old_name, new_name) {
+        const note = this.#get_note(old_name);
+        if (this.#notes.has(new_name)) {
+            throw new Error(`failed to rename to an existing note \"${new_name}\"`);
+        }
+
+        this.#notes.delete(old_name);
+        note.name = new_name;
+        this.#notes.set(note.name, note);
+    }
+
+    async write(name, content) {
+        const note = this.#get_note(name);
+        note.content = content;
+    }
+
+    async remove(name) {
+        if (this.#notes.has(name)) {
+            this.#notes.delete(name);
+        }
+    }
+
+    async read_tags(name) {
+        const note = this.#get_note(name);
+        return note.tags;
+    }
+
+    async write_tags(name, tags) {
+        const note = this.#get_note(name);
+        note.tags = tags;
+    }
+}
+
+export { FakeNoteProvider }
\ No newline at end of file
diff --git a/frontend/main.js b/frontend/main.js
index 145a3b5..b3057f7 100644
--- a/frontend/main.js
+++ b/frontend/main.js
@@ -8,11 +8,19 @@ import { marked } from "marked"
 import { slider_attach } from "./slider.js"
 import { init_titlebar } from "./titlebar.js"
 import { init_settings } from "./settings.js"
+import { FakeNoteProvider } from "./fakenoteprovider.js"
+import { NoteList } from "./notelist.js"
 
 init_titlebar();
 init_settings();
 slider_attach(document.querySelector("#slider"));
 
+const noteProvider = new FakeNoteProvider();
+const notelist_element = document.querySelector("#notelist");
+const notelist = new NoteList(noteProvider, notelist_element);
+document.querySelector("#add-note").addEventListener("click", async () => {
+  notelist.add_new();
+})
 
 const language = new Compartment();
 const editor_element = document.querySelector("#editor");
diff --git a/frontend/note.js b/frontend/note.js
new file mode 100644
index 0000000..ad61f64
--- /dev/null
+++ b/frontend/note.js
@@ -0,0 +1,26 @@
+class Note {
+    #name;
+    #list_item;
+
+    constructor(name, notelist) {
+        this.#name = name;
+        this.#create_listentry(notelist);
+    }
+
+
+    get name() {
+        return this.#name;
+    }
+
+    #create_listentry(notelist) {
+        this.#list_item = document.createElement("li");
+        notelist.element.appendChild(this.#list_item);
+
+        this.#list_item.textContent = this.#name;
+        this.#list_item.addEventListener('click', async () => {
+            console.log("ToDo: activate");
+        }, false);
+    }
+}
+
+export { Note }
diff --git a/frontend/notelist.js b/frontend/notelist.js
new file mode 100644
index 0000000..21f6c11
--- /dev/null
+++ b/frontend/notelist.js
@@ -0,0 +1,42 @@
+import { Note } from "./note.js"
+
+class NoteList {
+
+    #provider
+    #element;
+    #notes;
+
+    constructor(provider, element) {
+        this.#provider = provider;
+        this.#element = element;
+
+        this.#update();
+    }
+
+    async #update() {
+        this.#element.innerHTML = "";
+        this.#notes = new Map();
+
+        const notes = await this.#provider.list();
+        for (const name of notes) {
+            await this.#add(name);
+        }
+    }
+
+    async #add(name) {
+        const text = await this.#provider.read(name);
+        const tags = await this.#provider.read_tags(name);
+        const note = new Note(name, this);
+    }
+
+    get element() {
+        return this.#element;
+    }
+
+    async add_new() {
+        const name = await this.#provider.create();
+        this.#add(name);
+    }
+}
+
+export { NoteList }
diff --git a/frontend/noteprovider.js b/frontend/noteprovider.js
new file mode 100644
index 0000000..f73de23
--- /dev/null
+++ b/frontend/noteprovider.js
@@ -0,0 +1,117 @@
+
+/**
+ * This class documents the NoteProvider interface.
+ */
+class NoteProvider {
+
+    /**
+     * Returns a list of the names of all notes.
+     * 
+     * @returns {String[]} List containing the names of all notes.
+     */
+    async list() {
+        throw new Error("Not implementd");
+    }
+
+    /**
+     * Returns the contents of a note.
+     * 
+     * @param {String} name Name of the the note.
+     * @returns {String} contents of the note
+     * @throws {Error} unknown note
+     */
+    async read(name) {
+        throw new Error("Not implementd");
+    }
+
+    /**
+     * Creates a new note and returns it's name.
+     * 
+     * @returns {String} Name of the newly created note.
+     */
+    async create() {
+        throw new Error("Not implementd");
+    }
+
+    /**
+     * Renames an existing note.
+     * 
+     * @param {String} old_name Name of an existing note
+     * @param {String} new_name New name of the note
+     * @throws {Error} unknown note
+     * @throws {Error} there is already a note with the new name
+     */
+    async rename(old_name, new_name) {
+        throw new Error("Not implementd");
+    }
+
+    /**
+     * Writes the contents of an existing note.
+     * 
+     * @param {String} name Name of the note 
+     * @param {String} content New contents of the note
+     * @throws {Error} unknown note
+     */
+    async write(name, content) {
+        throw new Error("Not implementd");
+    }    
+
+    /**
+     * Removes an existing note.
+     * 
+     * Note that this method does not fail, if
+     * the note does not exist.
+     * 
+     * @param {String} name 
+     */
+    async remove(name) {
+        throw new Error("Not implementd");
+    }
+
+    /**
+     * Returns all tags of an existing note. 
+     * 
+     * @param {String} name name of then note
+     * @returns {String[]} tags of the note
+     * @throws {Error} unknown note
+     */
+    async read_tags(name) {
+        throw new Error("Not implementd");
+    }
+
+    /**
+     * Writes (replaces) tht tags of an existing note
+     * 
+     * @param {String} name name of the note
+     * @param {String[]} tags tags of the note
+     * @throws {Error} unknown note
+     */
+    async write_tags(name, tags) {
+        throw new Error("Not implementd");
+    }
+
+    /**
+     * Takes a screenshot and returns its filename.
+     * 
+     * @param {String} name name of the note associated with the screenshot
+     * @returns {String} filename of the screenshot (relative to the note's directory)
+     * @throws {Error} unknown note
+     * @throws {Error} screenshot command failed
+     */
+    async take_screenshot(name) {
+        throw new Error("Not implemented");
+    }
+
+    /**
+     * Opens the note directory in a file browser.
+     * 
+     * @param {String} name name of the note associated with the screenshot
+     * @throws {Error} unknown note
+     */
+    async open_note_directory(name) {
+        throw new Error("Not implemented");
+    }
+}
+
+
+export { NoteProvider }
\ No newline at end of file