# Write and Read Variables and Tables

Read and write persistent values in variables and tables with `zw.getRef()` and `zw.setRef()`.

**API** `zw.getRef()`, `zw.setRef()`

```js
// Get a value from a table column or a variable
const profileName = await zw.getRef({ ref_id: 5079, name: "Profile Name" });

// Save a value to a table column or a variable
await zw.setRef({ ref_id: 4372, name: "Profile Name Copy", value: profileName });
```

***

#### At a Glance

* <mark style="color:$info;">async</mark>\
  `await zw.getRef({ ref_id: number, name: string })` → `string`\
  → returns the `string` value from a variable or table column.
* <mark style="color:$info;">async/sync\*</mark> \
  `await zw.setRef({ ref_id: number, name: string, value: string })` → `void`\
  → saves a `string` value to a variable or table column.

\*`async` in browser, `sync` locally (see [/pages/1CmxmmRih9Urxyv0GTdm#local-vs.-browser-execution](https://docs.zerowork.io/using-zerowork/using-building-blocks/write-javascript/pages/1CmxmmRih9Urxyv0GTdm#local-vs.-browser-execution "mention")).

***

#### What’s `ref_id`?

In ZeroWork, **tables** and **variables** are data groups. Each data group has an ID called `ref_id`.

* **Table `ref_id`** is shown in the left sidebar next to the table name.\
  ![](/files/EPgOAFhjkO7QnaCqF2NU)<br>

* **Variables `ref_id`** is shown in the Variables dialog.

  <figure><img src="/files/2att8rQSEuhNYijBxapq" alt=""><figcaption></figcaption></figure>

* **`name`** identifies a table column or a variable in the Variables group. Names are **case-sensitive**.

  ```javascript
  // Read a table column by its table ref_id and column name
  const profileName = await zw.getRef({ ref_id: 5079, name: "Profile Name" });

  // Same variables ref_id for different variable names
  const date = await zw.getRef({ ref_id: 4379, name: "date" });
  const counter = await zw.getRef({ ref_id: 4379, name: "counter" });
  ```

***

#### Saving Non-String Values

Values are stored as **strings**. To store arrays or objects, **stringify on write** and **parse on read**. Passing a non-string `value` to `zw.setRef()` throws an error.

```javascript
// 🚫 Won't work (value must be a string)
await zw.setRef({ ref_id: 4379, name: "chars", value: ["a", "b", "c"] });
// Error: The value argument must be a string.
```

**Example**

```js
// Save objects or arrays (stringify on write)
await zw.setRef({
  ref_id: 4379,
  name: "animals",
  value: JSON.stringify([{ category: "cat" }, { category: "dog" }]),
});

// Read and use it (parse on read)
const animalsArray = JSON.parse(
  await zw.getRef({ ref_id: 4379, name: "animals" })
);
```

***

#### Loops and Row Behavior

`zw.getRef()` and `zw.setRef()` follow ZeroWork’s loop logic:

* **Read existing rows** → place **Start Repeat** with **dynamic** loop **before** your Write JavaScript building block.
* **Append new rows** → place **Start Repeat** with **standard** loop before the block **or** use a custom append with `appendIndex`.

Read more about how loops work in ZeroWork: [Start Repeat](/using-zerowork/using-building-blocks/start-repeat.md).&#x20;

**Custom append without Start Repeat**

Use `appendIndex` to add rows from a loop in your code.\
`appendIndex: 0` is the first appended row. If a table already has 3 rows, `appendIndex: 0` becomes the 4th row.

> ⚠️ When using `appendIndex`, placing this Write JavaScript building block inside a Start Repeat loop can mix the built-in loop index with a custom `appendIndex`, causing mismatched rows.

**Example**

```js
const valuesToSave = ["a", "b", "c"];
const TABLE_REF_ID = 4382;

let i = 0;
for (const val of valuesToSave) {
  await zw.setRef({
    ref_id: TABLE_REF_ID,
    name: "value",
    value: val,
    appendIndex: i,
  });
  i++;
}
```

The resulting table:\
![](/files/u21KrkZtKpbfhSCN4eZt)

**Update existing rows in a custom code loop**

Add a **dynamic** Start Repeat building block to iterate rows and use the Write JavaScript building block within that loop. As long as your Write JavaScript is inside that loop, it will have access to the values inside the row that is currently being iterated over. If you need true in-code row access, share your use case in [Discord](https://discord.gg/JkkCNmVBeg) in the #ideas-feature-suggestions channel.

***

#### Common Pitfalls

* **Always use `await` in the browser.**\
  In the browser, `zw.getRef()` and `zw.setRef()` are async — use `await`.&#x20;

  ```javascript
  // 🚫 Won't work (running in the browser)
  const name = zw.getRef({ ref_id: 5079, name: "Profile Name" }); // missing await → name is a Promise, not a string
  await zw.setRef({ ref_id: 4379, name: "Copy", value: name }); // throws error
  // Error: The value argument must be a string.

  // ✅ Works
  const name2 = await zw.getRef({ ref_id: 5079, name: "Profile Name" });
  await zw.setRef({ ref_id: 4379, name: "Copy", value: name2 });
  ```
* **Match the exact name (case-sensitive).**

  ```javascript
  // 🚫 Won't work (case mismatch)
  await zw.getRef({ ref_id: 5079, name: "profile name" });
  // Error: This variable or column does not exist.

  // ✅ Works
  await zw.getRef({ ref_id: 5079, name: "Profile Name" });
  ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zerowork.io/using-zerowork/using-building-blocks/write-javascript/write-and-read-variables-and-tables.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
