diff --git a/package-lock.json b/package-lock.json
index 36384312f..a4059c2bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4701,6 +4701,24 @@
       "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==",
       "dev": true
     },
+    "node_modules/@types/lodash.debounce": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz",
+      "integrity": "sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==",
+      "dev": true,
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@types/lodash.memoize": {
+      "version": "4.1.7",
+      "resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.7.tgz",
+      "integrity": "sha512-lGN7WeO4vO6sICVpf041Q7BX/9k1Y24Zo3FY0aUezr1QlKznpjzsDk3T3wvH8ofYzoK0QupN9TWcFAFZlyPwQQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
     "node_modules/@types/mdast": {
       "version": "3.0.12",
       "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz",
@@ -9905,6 +9923,11 @@
       "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
       "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
     },
+    "node_modules/lodash.memoize": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
+    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -15744,7 +15767,7 @@
         "@typecell-org/util": "^0.0.3",
         "classnames": "^2.3.1",
         "fractional-indexing": "^2.0.0",
-        "lodash": "^4.17.21",
+        "lodash.memoize": "^4.1.2",
         "mobx": "^6.2.0",
         "mobx-react-lite": "^3.2.0",
         "penpal": "^6.1.0",
@@ -15768,7 +15791,7 @@
         "@playwright/experimental-ct-react": "^1.36.0",
         "@playwright/test": "^1.33.0",
         "@typecell-org/shared-test": "^0.0.3",
-        "@types/lodash": "^4.14.168",
+        "@types/lodash.memoize": "^4.1.7",
         "@types/node": "^16.0.0",
         "@types/react": "^18.0.25",
         "@types/react-dom": "^18.0.9",
@@ -16078,7 +16101,8 @@
       "dependencies": {
         "@typecell-org/shared": "*",
         "es-module-shims": "1.4.3",
-        "lodash": "^4.17.21",
+        "lodash.debounce": "^4.0.8",
+        "lodash.memoize": "^4.1.2",
         "mobx": "^6.2.0",
         "react": "^18.2.0",
         "vscode-lib": "^0.1.2"
@@ -16086,7 +16110,8 @@
       "devDependencies": {
         "@playwright/test": "^1.33.0",
         "@types/chai": "^4.3.0",
-        "@types/lodash": "^4.14.182",
+        "@types/lodash.debounce": "^4.0.7",
+        "@types/lodash.memoize": "^4.1.7",
         "@types/mocha": "^10.0.1",
         "@types/react": "^18.0.25",
         "@vitest/coverage-v8": "^0.33.0",
diff --git a/packages/editor/package.json b/packages/editor/package.json
index 838f9ce0f..a89232455 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -39,7 +39,7 @@
     "speakingurl": "^14.0.1",
     "classnames": "^2.3.1",
     "fractional-indexing": "^2.0.0",
-    "lodash": "^4.17.21",
+    "lodash.memoize": "^4.1.2",
     "mobx": "^6.2.0",
     "mobx-react-lite": "^3.2.0",
     "penpal": "^6.1.0",
@@ -103,7 +103,7 @@
     "@playwright/test": "^1.33.0",
     "@playwright/experimental-ct-react": "^1.36.0",
     "@typecell-org/shared-test": "^0.0.3",
-    "@types/lodash": "^4.14.168",
+    "@types/lodash.memoize": "^4.1.7",
     "@types/node": "^16.0.0",
     "@types/react": "^18.0.25",
     "@types/react-dom": "^18.0.9",
diff --git a/packages/editor/src/store/yjs-sync/remote/FetchRemote.ts b/packages/editor/src/store/yjs-sync/remote/FetchRemote.ts
index b5555b770..c7fa73b51 100644
--- a/packages/editor/src/store/yjs-sync/remote/FetchRemote.ts
+++ b/packages/editor/src/store/yjs-sync/remote/FetchRemote.ts
@@ -2,7 +2,7 @@ import { makeObservable, observable, runInAction } from "mobx";
 import { path, strings } from "vscode-lib";
 
 import { ChildReference, IndexFileReference } from "@typecell-org/shared";
-import _ from "lodash";
+import memoize from "lodash.memoize";
 import * as Y from "yjs";
 import { filesToTreeNodes } from "../../../app/documentRenderers/project/directoryNavigation/treeNodeUtil";
 import { HttpsIdentifier } from "../../../identifiers/HttpsIdentifier";
@@ -48,7 +48,7 @@ export default class FetchRemote extends Remote {
 
   public constructor(
     _ydoc: Y.Doc,
-    private readonly identifier: HttpsIdentifier
+    private readonly identifier: HttpsIdentifier,
   ) {
     super(_ydoc);
     makeObservable(this, {
@@ -85,7 +85,7 @@ export default class FetchRemote extends Remote {
     const project = new ProjectResource(newDoc, this.identifier); // TODO
 
     const tree = filesToTreeNodes(
-      indexFile.items.map((object) => ({ fileName: object }))
+      indexFile.items.map((object) => ({ fileName: object })),
     );
 
     tree.forEach((node) => {
@@ -100,7 +100,7 @@ export default class FetchRemote extends Remote {
     return newDoc;
   }
 
-  private fetchIndex = _.memoize(async (path: string) => {
+  private fetchIndex = memoize(async (path: string) => {
     return (await (await fetch(path)).json()) as IndexFile;
   });
 
@@ -115,7 +115,7 @@ export default class FetchRemote extends Remote {
 
       return markdownToYDoc(
         contents,
-        decodeURIComponent(path.basename(this.identifier.uri.path))
+        decodeURIComponent(path.basename(this.identifier.uri.path)),
       );
     } else {
       // TODO: this is hacky. We should use json from parent route instead. Revise routing?
diff --git a/packages/engine/package.json b/packages/engine/package.json
index 6de60cf0f..99f85b082 100644
--- a/packages/engine/package.json
+++ b/packages/engine/package.json
@@ -4,7 +4,8 @@
   "private": true,
   "dependencies": {
     "es-module-shims": "1.4.3",
-    "lodash": "^4.17.21",
+    "lodash.debounce": "^4.0.8",
+    "lodash.memoize": "^4.1.2",
     "mobx": "^6.2.0",
     "react": "^18.2.0",
     "vscode-lib": "^0.1.2",
@@ -15,8 +16,9 @@
     "mocha": "^10.2.0",
     "@types/chai": "^4.3.0",
     "@types/mocha": "^10.0.1",
-    "@types/lodash": "^4.14.182",
     "@types/react": "^18.0.25",
+    "@types/lodash.memoize": "^4.1.7",
+    "@types/lodash.debounce": "^4.0.7",
     "rimraf": "^3.0.2",
     "typescript": "5.0.4",
     "@playwright/test": "^1.33.0",
diff --git a/packages/engine/src/ReactiveEngine.ts b/packages/engine/src/ReactiveEngine.ts
index 9a369a27b..3e2bafdea 100644
--- a/packages/engine/src/ReactiveEngine.ts
+++ b/packages/engine/src/ReactiveEngine.ts
@@ -1,6 +1,6 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
 import { CodeModel } from "@typecell-org/shared/src/codeModels/CodeModel.js";
-import _ from "lodash";
+import debounce from "lodash.debounce";
 import { event, lifecycle } from "vscode-lib";
 import { createCellEvaluator } from "./CellEvaluator.js";
 import { TypeCellContext, createContext } from "./context.js";
@@ -57,9 +57,9 @@ export class ReactiveEngine<T extends CodeModel> extends lifecycle.Disposable {
   constructor(
     private resolveImport: (
       module: string,
-      forModel: T
+      forModel: T,
     ) => Promise<ResolvedImport | undefined>,
-    private debounceMillis = 100
+    private debounceMillis = 100,
   ) {
     super();
   }
@@ -72,7 +72,7 @@ export class ReactiveEngine<T extends CodeModel> extends lifecycle.Disposable {
     this._register(
       compiledModelProvider.onDidCreateModel((m) => {
         this.registerModel(m);
-      })
+      }),
     );
   }
 
@@ -107,13 +107,13 @@ export class ReactiveEngine<T extends CodeModel> extends lifecycle.Disposable {
           }
           return ret.module;
         },
-        (model, output) => this._onOutput.fire({ model, output })
+        (model, output) => this._onOutput.fire({ model, output }),
       ); // catch errors?
     };
     let prevValue: string | undefined = model.getValue();
 
     // TODO: maybe only debounce (or increase debounce timeout) if an execution is still pending?
-    const reEvaluate = _.debounce(() => {
+    const reEvaluate = debounce(() => {
       if (model.getValue() !== prevValue) {
         // make sure there were actual changes from the previous value
 
@@ -138,7 +138,7 @@ export class ReactiveEngine<T extends CodeModel> extends lifecycle.Disposable {
           evaluator.dispose();
           this.evaluatorCache.delete(model);
         }
-      })
+      }),
     );
   }
 
@@ -156,7 +156,7 @@ export class ReactiveEngine<T extends CodeModel> extends lifecycle.Disposable {
     model: T,
     typecellContext: TypeCellContext<any>,
     resolveImport: (module: string) => Promise<any>,
-    onOutput: (model: T, output: any) => void
+    onOutput: (model: T, output: any) => void,
   ) {
     if (!this.evaluatorCache.has(model)) {
       this.evaluatorCache.set(
@@ -166,8 +166,8 @@ export class ReactiveEngine<T extends CodeModel> extends lifecycle.Disposable {
           resolveImport,
           true,
           (output) => onOutput(model, output),
-          () => this._onBeforeExecution.fire({ model })
-        )
+          () => this._onBeforeExecution.fire({ model }),
+        ),
       );
     }
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
diff --git a/packages/engine/src/resolvers/ImportShimResolver.ts b/packages/engine/src/resolvers/ImportShimResolver.ts
index 1b377e315..934176439 100644
--- a/packages/engine/src/resolvers/ImportShimResolver.ts
+++ b/packages/engine/src/resolvers/ImportShimResolver.ts
@@ -1,5 +1,5 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
-import _ from "lodash";
+import memoize from "lodash.memoize";
 import { ExternalModuleResolver } from "./ExternalModuleResolver.js";
 import { LocalModuleResolver } from "./LocalModuleResolver.js";
 
@@ -7,7 +7,7 @@ let importShimCreated = false;
 
 function createBlob(source: string) {
   return URL.createObjectURL(
-    new Blob([source], { type: "application/javascript" })
+    new Blob([source], { type: "application/javascript" }),
   );
 }
 
@@ -16,11 +16,11 @@ export class ImportShimResolver {
 
   public constructor(
     private readonly resolvers: ExternalModuleResolver[],
-    private readonly localResolver: LocalModuleResolver
+    private readonly localResolver: LocalModuleResolver,
   ) {
     if (importShimCreated) {
       throw new Error(
-        "only 1 ImportShimResolver can exists because it uses a global importShim"
+        "only 1 ImportShimResolver can exists because it uses a global importShim",
       );
     }
     importShimCreated = true;
@@ -30,7 +30,7 @@ export class ImportShimResolver {
    * Resolve a moduleName (e.g.: "lodash", "d3") to the actual module
    * using the resolvers passed to the constructor
    */
-  public resolveImport = _.memoize(this.doResolveImport);
+  public resolveImport = memoize(this.doResolveImport);
 
   private async doResolveImport(moduleName: string) {
     await this.initializeImportShim();
@@ -56,7 +56,7 @@ export class ImportShimResolver {
     for (const resolver of this.resolvers) {
       try {
         const module = await this.importShim(
-          "use$" + resolver.name + "$" + moduleName
+          "use$" + resolver.name + "$" + moduleName,
         );
         console.log("loaded module", moduleName, "using", resolver.name);
         return {
@@ -79,7 +79,7 @@ export class ImportShimResolver {
     resolver: ExternalModuleResolver,
     moduleName: string, // can be a relative URL, absolute URL, or "package name"
     parent: string, // the parent URL the package is loaded from
-    importShimResolve: any // the original resolve function from es-module-shims
+    importShimResolve: any, // the original resolve function from es-module-shims
   ): Promise<string> {
     // first try to see if the LocalResolver wants to resolve this module
     const localURL = await this.tryLocalResolver(moduleName);
@@ -107,7 +107,7 @@ export class ImportShimResolver {
           // that we want to override with a package from LocalResolver
           const localURL = await this.tryLocalResolver(
             parsedModule.module,
-            parsedModule.mode
+            parsedModule.mode,
           );
           if (localURL) {
             return localURL;
@@ -121,7 +121,7 @@ export class ImportShimResolver {
                 resolver,
                 "maplibre-gl",
                 parent,
-                importShimResolve
+                importShimResolve,
               );
             }
           }
@@ -142,7 +142,7 @@ export class ImportShimResolver {
 
   private async tryLocalResolver(
     moduleName: string,
-    mode?: string
+    mode?: string,
   ): Promise<string | undefined> {
     const local = await this.localResolver.getModule(moduleName, mode);
     if (local) {
@@ -169,11 +169,11 @@ export class ImportShimResolver {
   private getLocalURLForModule(
     loadedModule: any,
     moduleName: string,
-    mode?: string
+    mode?: string,
   ) {
     const safeName = (moduleName + "/" + mode).replaceAll(
       /[^a-zA-Z0-9_]/g,
-      "$"
+      "$",
     );
 
     if ((window as any)["__typecell_url_" + safeName]) {
@@ -183,7 +183,7 @@ export class ImportShimResolver {
 
     (window as any)["__typecell_pkg_" + safeName] = loadedModule;
     const list = Object.keys(loadedModule).filter(
-      (key) => key !== "default" && key !== "window"
+      (key) => key !== "default" && key !== "window",
     );
 
     const url = createBlob(`
@@ -202,7 +202,7 @@ export class ImportShimResolver {
   private onImportShimResolve = async (
     id: string,
     parent: string,
-    importShimResolve: any
+    importShimResolve: any,
   ) => {
     // by default, try the first resolver
     let resolver = this.resolvers[0];
@@ -221,7 +221,7 @@ export class ImportShimResolver {
         // when there is a parent, this is expected (because it's a nested module).
         console.warn(
           "no explicit resolver detected in import, falling back to default",
-          id
+          id,
         );
       }
     }
@@ -230,7 +230,7 @@ export class ImportShimResolver {
       resolver,
       id,
       parent,
-      importShimResolve
+      importShimResolve,
     );
     return ret;
   };
diff --git a/packages/packager/package.json b/packages/packager/package.json
index db450fc65..70c2d4f6a 100644
--- a/packages/packager/package.json
+++ b/packages/packager/package.json
@@ -7,7 +7,6 @@
     "@typecell-org/engine": "*",
     "@typecell-org/parsers": "*",
     "es-module-shims": "1.4.3",
-    "lodash": "^4.17.21",
     "mobx": "^6.2.0",
     "react": "^18.2.0",
     "vscode-lib": "^0.1.2",
@@ -19,7 +18,6 @@
     "@types/fs-extra": "9.0.13",
     "@types/chai": "^4.3.0",
     "@types/mocha": "^10.0.1",
-    "@types/lodash": "^4.14.182",
     "@types/react": "^18.0.25",
     "rimraf": "^3.0.2",
     "typescript": "5.0.4",
