> For the complete documentation index, see [llms.txt](https://docs.zerowork.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zerowork.io/using-zerowork/using-building-blocks/write-javascript.md).

# Write JavaScript

Use the **Write JavaScript** building block to run custom code with the `zw` API. Your code runs inside the TaskBot runtime and can work with npm packages, variables and tables, state, device storage, and the browser context.

### What You Can Do

#### Imports and Package Management

Install, import, manage, and use npm packages. Use standard `import` statements or `zw.import(package, options)` for custom options.\
**API** `zw.import()`, `zw.packages.*`

```javascript
// @zw-run-locally

import dayjs from "dayjs@1.11.11"; // auto-installs if needed
await zw.log("now", dayjs().toISOString());

// Example with options — 'isolate' scopes the package to this TaskBot only
const lodash = await zw.import("lodash@4.17.21", { isolate: true });
await zw.log("chunked result", lodash.chunk([1, 2, 3, 4], 2));
```

**Reference:** [Imports and Package Management](/using-zerowork/using-building-blocks/write-javascript/imports-and-package-management.md)

***

#### Variables and Tables

Read from and write to variables and tables (native and Google Sheets).\
**API** `zw.getRef()`, `zw.setRef()`

```javascript
const email = await zw.getRef({ ref_id: 3623, name: "Email" });
await zw.setRef({ ref_id: 3624, name: "Email copy", value: email });
```

**Reference:** [Write and Read Variables and Tables](/using-zerowork/using-building-blocks/write-javascript/write-and-read-variables-and-tables.md)

***

#### Local and Global State

Store and use values for the current run (local state) or across runs (global state). Stays on this device; never sent to ZeroWork servers.\
**API** `zw.state.*`, `zw.globalState.*`

```javascript
// @zw-run-locally 

// Per-run local state, cleared when the run ends
const runState = zw.state.access();
runState.runStartedAt = Date.now();

// Shared Desktop Agent global state, cleared when Agent quits
const globalState = zw.globalState.access();
globalState.totalRuns = (globalState.totalRuns ?? 0) + 1;
```

```javascript
// In-browser snapshot (survives serialization)
const snapshot = await zw.state.browser.getCopy();
snapshot.runStartedAt = Date.now();
await zw.state.browser.commit(snapshot);
```

**Reference:** [Local and Global State](/using-zerowork/using-building-blocks/write-javascript/local-and-global-state.md)

***

#### Device Storage

Persist key–value data on this device. Survives restarts and reinstalls. Stays on this device; never sent to ZeroWork servers.\
**API** `zw.deviceStorage.*`

```javascript
const credentials = {
  username: await zw.deviceStorage.get("username_salesforce"),
  password: await zw.deviceStorage.get("password_salesforce"),
};
await zw.deviceStorage.set("last_login_salesforce", String(Date.now()));
```

**Reference:** [Device Storage](/using-zerowork/using-building-blocks/write-javascript/device-storage.md)

***

#### Utilities

Add delays and write logs.\
**API** `zw.delay()`, `zw.log()`, `zw.logTemp()`

```javascript
await zw.log("starting run");
await zw.delay({ min: 1500, max: 2500 });
```

**Reference:** [Utilities](/using-zerowork/using-building-blocks/write-javascript/utilities.md)

***

#### Browser and Context

Launch, quit, and manage the active browser context and pages.\
**API** `zw.browserContext.*`

```javascript
// @zw-run-locally

// Example (abridged). See the reference for all options.
await zw.browserContext.launch({
  launchConfig: {
    mode: "incognito", // or "sticky"
    bypassDetection: true,
    launchOptions: { headless: true /* proxy, executablePath, ... */ },
    contextOptions: { /* viewport, recordVideo, geolocation, ... */ },
    scripts: [],
    cookies: [],
    onContextReady: async (context) => {
      await context.route('**/*.{png,jpg,jpeg}', route => route.abort());
    },
  }
  // see the reference for the full list of options
});

const contextInfo = await zw.browserContext.getContextInfo();
await zw.log("context", contextInfo);
await zw.browserContext.quit();
```

**Reference:** [Browser Context](/using-zerowork/using-building-blocks/write-javascript/browser-context.md)

***

#### Metadata

Read TaskBot and ZeroWork Desktop Agent details.\
**API** `zw.getTaskbotInfo()`, `zw.getAgentInfo()`

```javascript
const taskbotInfo = await zw.getTaskbotInfo();
await zw.log("TaskBot name", taskbotInfo.name);
await zw.log("variables", taskbotInfo.variables);

const agent = zw.getAgentInfo();
await zw.log("agent version", agent.version);
```

**Reference:** [Metadata](/using-zerowork/using-building-blocks/write-javascript/metadata.md)

***

### **Local vs. Browser Execution**

Some methods only work when the code runs locally.

To run code locally, either enable the **Run locally** checkbox in the building block UI or include `// @zw-run-locally` anywhere in your code (top is recommended for visibility).

**Example**

```javascript
// @zw-run-locally
await zw.log("This runs locally now");
console.log("And this will no longer be visible in the browser console.");
```

* **Fully available in browser execution**
  * **Variables and tables** — `zw.getRef()`, `zw.setRef()`
  * **Device storage** — `zw.deviceStorage.*`
  * **Utilities** — `zw.delay()`, `zw.log()`, `zw.logTemp()`
  * **Metadata** — `zw.getTaskbotInfo()`, `zw.getAgentInfo()`
* **Available in browser with limitations**
  * **Imports and package management** — `zw.import()`, `zw.packages.*` \
    Installing and managing packages is only available when running locally; using already imported packages is supported in the browser — see how [**here**](/using-zerowork/using-building-blocks/write-javascript/imports-and-package-management.md#how-to-use-imported-packages-in-the-browser).
  * **Local and global state** — `zw.state.*`, `zw.globalState.*` \
    Access to the live mutable object via `*.access()` is **only** available locally. In the browser, you can get a snapshot with `*.browser.getCopy()` and save state with `*.browser.commit()` — see how [**here**](https://docs.zerowork.io/using-zerowork/using-building-blocks/pages/DI5u1TjzGfw12nL7h8V2#id-3.-use-state-in-the-browser-zw.state.browser.-and-zw.globalstate.browser).\
    The clear method `*.clear()` is available in both local and browser execution.
  * **Browser context** — `zw.browserContext.*`\
    Largely unavailable in browser execution. Only inspection methods `zw.browserContext.getContextInfo()` and `zw.browserContext.getDefaults()` are supported in the browser.

***

### **Using the `zw` APIs in No-Code Blocks**

You can use the `zw` APIs in no-code blocks, too, either as expressions `${...}` or as code blocks with `return` statements `$${...}`.

**Example**

<div align="left"><figure><img src="/files/cDz3BYs78HKmJahGa77p" alt=""><figcaption></figcaption></figure></div>

**Reference**:  [Code in Inputs](/using-zerowork/using-building-blocks/dynamic-inputs/code-in-inputs.md)

***

### Error Handling

Thrown errors are automatically saved to **Error Reports** in **Run Reports** (see [Using Run Reports](/using-zerowork/using-run-reports.md)). Notifications, if enabled, then follow your error run notification settings (by email, Slack, or a custom webhook). This is a useful way to generate custom errors and get notified automatically.

**Example**

```javascript
throw new Error(
  "Test error to verify Error Reports in Run Reports"
);
```

**Resulting error report**

<div align="left"><figure><img src="/files/RRdPa4Oh1QmCqT6rCK7B" alt="" width="563"><figcaption></figcaption></figure></div>

Thrown errors can also be caught by the **Try-Catch** building blocks, see: [Try-Catch](/using-zerowork/using-building-blocks/try-catch.md).

***

### Notes

* Old API `log()`, `delay()`, `setRef()`, and `getRef()` (without the `zw.` prefix) still work. We recommend the new convention `zw.*` (e.g., `zw.getRef()`), but your current code will continue to run. Existing `activePage` and `taskbotContext` will also continue to work, although they're now discouraged — prefer `zw.browserContext.getActivePage()` and `zw.browserContext.getContext()`.
