diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..e6e758ce34734159063c8b9fc36b7007cb5b30e7
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,38 @@
+{
+    "parser": "@babel/eslint-parser",
+    "env": {
+        "node": true,
+        "browser": true,
+        "es2021": true,
+        "jest/globals": true
+    },
+    "extends": [
+        "eslint:recommended",
+        "plugin:react/recommended",
+        "plugin:react-hooks/recommended",
+        "plugin:jsx-a11y/recommended"
+    ],
+    "overrides": [
+    ],
+    "parserOptions": {
+        "ecmaVersion": "latest",
+        "sourceType": "module"
+    },
+    "ignorePatterns": [
+        "*.scss",
+        "*.snap",
+        "*.json"
+    ],
+    "plugins": [
+        "react",
+        "jsx-a11y",
+        "jest"
+    ],
+    "rules": {
+    },
+    "settings": {
+        "react": {
+            "version": "detect"
+        }
+    }
+}
diff --git a/package.json b/package.json
index e2505c814d7c4e5a4dce0c674097f8d68483d507..a13ecceda6a59729629f9750b624cb7f79804d09 100644
--- a/package.json
+++ b/package.json
@@ -16,13 +16,20 @@
   },
   "devDependencies": {
     "@babel/core": "^7.21.3",
+    "@babel/eslint-parser": "^7.21.3",
     "@babel/preset-env": "^7.20.2",
     "@babel/preset-react": "^7.18.6",
     "babel-loader": "^9.1.2",
     "bootstrap": "^5.2.3",
     "bootstrap-icons": "^1.10.3",
     "css-loader": "^6.7.3",
+    "eslint": "^8.36.0",
+    "eslint-plugin-jest": "^27.2.1",
+    "eslint-plugin-jsx-a11y": "^6.7.1",
+    "eslint-plugin-react": "^7.32.2",
+    "eslint-plugin-react-hooks": "^4.6.0",
     "html-webpack-plugin": "^5.5.0",
+    "prettier": "^3.0.3",
     "source-map-loader": "^4.0.1",
     "style-loader": "^3.3.2",
     "webpack": "^5.76.3",
@@ -32,7 +39,9 @@
   "scripts": {
     "start": "webpack-dev-server --mode=development --open --hot",
     "build": "webpack --mode=production",
-    "build:dev": "webpack --mode=development"
+    "build:dev": "webpack --mode=development",
+    "lint": "eslint \"src/**\"",
+    "format": "prettier -u -w \"src/\""
   },
   "author": "Timm Fitschen",
   "license": "AGPL-3.0-or-later"
diff --git a/src/map.js b/src/map.js
index 2db53b02b122708bf8926b5671a5cfc12ef110e1..4434b480b6fbbd055b349e9bef47a2059e65782c 100644
--- a/src/map.js
+++ b/src/map.js
@@ -44,7 +44,7 @@ const caosdb_map_2 = {
     map_root.render(
       <React.StrictMode>
         <Map queryCallback={queryCallback} />
-      </React.StrictMode>
+      </React.StrictMode>,
     );
 
     const navItem = this.create_navbar_item();
@@ -53,7 +53,7 @@ const caosdb_map_2 = {
     button_root.render(
       <React.StrictMode>
         <ToggleMapButton className="nav-link" />
-      </React.StrictMode>
+      </React.StrictMode>,
     );
   },
 };
diff --git a/src/query-form.js b/src/query-form.js
index dea4ec80b529db02e00acb0cc64287008849db66..9384fff5cb594122adf16ba1b6aeebc6fa593b76 100644
--- a/src/query-form.js
+++ b/src/query-form.js
@@ -14,7 +14,7 @@ function queryPanel(
   tabs,
   getSuggestionsCallback,
   restore,
-  defaultTab
+  defaultTab,
 ) {
   const element = document.createElement("DIV");
   document
@@ -36,7 +36,7 @@ function queryPanel(
       defaultTab={defaultTab}
       getSuggestionsCallback={getSuggestionsCallback}
       restore={restore}
-    />
+    />,
   );
 }
 
@@ -68,7 +68,7 @@ async function initQueryPanel() {
       function () {
         initQueryPanel();
       },
-      false
+      false,
     );
     return;
   }
@@ -78,7 +78,7 @@ async function initQueryPanel() {
   // for auto completion
   const retrieveNames = async () => {
     var response = $(
-      await connection.get(transaction.generateEntitiesUri(["names"]))
+      await connection.get(transaction.generateEntitiesUri(["names"])),
     ).find("Property[name],RecordType[name],Record[name]");
 
     response = response.toArray().map((x) => $(x).attr("name"));
@@ -114,8 +114,8 @@ async function initQueryPanel() {
         conf.label,
         conf.description,
         undefined,
-        conf.filterDefinitions
-      )
+        conf.filterDefinitions,
+      ),
     );
     if (prependAllTab && tabs_config.length > 0) {
       tabs_config = [
@@ -124,7 +124,7 @@ async function initQueryPanel() {
           undefined,
           "All",
           "Show all matching results",
-          makeQueryTemplate()
+          makeQueryTemplate(),
         ),
       ].concat(tabs_config);
     }
@@ -142,7 +142,7 @@ async function initQueryPanel() {
     tabs_config,
     getSuggestionsCallback,
     restore,
-    defaultTab
+    defaultTab,
   );
 }