If you aren't packing anything the browser natively treeshakes ESM for you. It doesn't load modules that aren't imported, for one obvious thing. In a "loose" package of ESM modules the browser may never even see most of the tree.
Beyond that ESM "module objects" (the proxy that the import represents and is most directly seen in an `import * as someModule` style import) in most of the browsers are also built to be readonly treeshakeable things in that they may not be immediately JIT compiled and are generally subject to garbage collection rules like everything else, and being readonly proxies may garbage collect sooner than average. So the browser will lazily "treeshake" by garbage collection at runtime in a running app. (Though if you are relying heavily on that it likely means your modules are too big and you should consider smaller, looser, less packed modules and relying on the first bit where stuff not imported is never seen by the browser.)
Beyond that ESM "module objects" (the proxy that the import represents and is most directly seen in an `import * as someModule` style import) in most of the browsers are also built to be readonly treeshakeable things in that they may not be immediately JIT compiled and are generally subject to garbage collection rules like everything else, and being readonly proxies may garbage collect sooner than average. So the browser will lazily "treeshake" by garbage collection at runtime in a running app. (Though if you are relying heavily on that it likely means your modules are too big and you should consider smaller, looser, less packed modules and relying on the first bit where stuff not imported is never seen by the browser.)