> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nimbleway.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript Rendering

> Control browser rendering for dynamic content and JavaScript execution

JavaScript rendering enables full browser execution to capture dynamically loaded content, handle user interactions, and process JavaScript-heavy websites. Combine it with [browser actions](/nimble-sdk/web-tools/extract/features/browser-actions) to control exactly when the page counts as "loaded".

## When to use

Use JavaScript rendering when you need to:

* **Load dynamic content**: Capture content loaded via AJAX or fetch requests
* **Execute JavaScript**: Process sites that require JS for content display
* **Handle SPAs**: Extract data from Single Page Applications (React, Vue, Angular)
* **Wait for interactions**: Capture DOM state after user actions
* **See final state**: Get the page as users see it, not raw HTML

## Parameters

<AccordionGroup>
  <Accordion title="render" icon="browser">
    <ParamField path="render" default="false" type="boolean | 'auto'">
      Turn JavaScript rendering on or off. When enabled, the page loads in a real browser that executes JavaScript, just like when you visit it yourself.

      **When to enable:**

      * Single Page Applications (React, Vue, Angular)
      * Content loaded via AJAX or fetch
      * Sites that need JavaScript to display content
      * When you need the page as users see it

      **When to keep disabled (faster, cheaper):**

      * Static HTML pages
      * Simple websites without JavaScript
      * When raw HTML is sufficient

      **Auto mode:** Set `render: "auto"` to let Nimble decide per request. The optimization engine starts with the fastest, cheapest configuration and automatically escalates through more capable driver configurations (including full rendering and stealth) until the request succeeds for the target domain.

      **Note:** Rendering requires vx8 or vx10. The vx6 driver doesn't support rendering.
    </ParamField>

    **Example:**

    ```json theme={"system"}
    "render": true
    ```

    **Example (automatic driver selection):**

    ```json theme={"system"}
    "render": "auto"
    ```
  </Accordion>

  <Accordion title="auto_driver_configuration" icon="wand-magic-sparkles">
    <ParamField path="auto_driver_configuration" type="object">
      Customize how automatic driver selection escalates. Maps driver configuration names to the number of attempts to spend on each before advancing to the next one (0 skips it). The key order defines the escalation order.

      Providing this parameter automatically opts the request into `render: "auto"` — you don't need to set `render` as well.

      **Available configuration names:**

      * `vx6-fast` - Plain HTTP request, no browser session (fastest)
      * `vx6-stealth` - HTTP request with a persistent browser session
      * `vx8` - Headless browser rendering
      * `vx8-pro` - Headful browser rendering
      * `vx10` - Stealth headless browser rendering
      * `vx10-pro` - Stealth browser rendering with a persistent session (most effective)

      **Rules:**

      * Attempts per configuration: 0-10
      * At least one configuration must have attempts > 0
      * If a specific `driver` is also set, it wins and `auto_driver_configuration` is ignored
    </ParamField>

    **Example:**

    ```json theme={"system"}
    "auto_driver_configuration": {
      "vx6-fast": 1,
      "vx8": 3,
      "vx10-pro": 2
    }
    ```

    This tries a plain HTTP request once, then headless rendering up to 3 times, then maximum stealth up to 2 times.
  </Accordion>

  <Accordion title="Waiting for content" icon="hourglass-half">
    Fine-grained load control lives in [browser actions](/nimble-sdk/web-tools/extract/features/browser-actions), which run inside the rendering browser:

    * `wait_for_navigation` - when to consider the page "loaded"
      * `load` - the standard page load event, good for most pages
      * `domcontentloaded` - as soon as HTML is ready, before images load (fastest)
      * `networkidle2` - 2 or fewer network requests in the last 500ms, good for dynamic content
      * `networkidle0` - zero network activity for 500ms (most thorough, slowest)
    * `wait_for_element` - wait until a specific CSS selector appears
    * `wait` - wait a fixed amount of time (e.g. `"2s"`)

    **Example:**

    ```json theme={"system"}
    "browser_actions": [
      { "wait_for_navigation": "networkidle0" },
      { "wait_for_element": ".product-grid" }
    ]
    ```
  </Accordion>
</AccordionGroup>

## Usage

### Enable basic rendering

Set `render: true` to enable JavaScript execution:

<CodeGroup>
  ```python Python theme={"system"}
  from nimble_python import Nimble

  nimble = Nimble(api_key="YOUR-API-KEY")

  result = nimble.extract.run(
      url="https://www.google.com/search?q=nba+allstars+2026",
      render=True
  )

  print(result.data.html)
  ```

  ```typescript TypeScript theme={"system"}
  import Nimble from "@nimble-way/nimble-js";

  const nimble = new Nimble({ apiKey: "YOUR-API-KEY" });

  const result = await nimble.extract.run({
    url: "https://www.google.com/search?q=nba+allstars+2026",
    render: true,
  });

  console.log(result.data?.html);
  ```

  ```bash cURL theme={"system"}
  curl -X POST 'https://sdk.nimbleway.com/v2/extract' \
  --header 'Authorization: Bearer <YOUR-API-KEY>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "url": "https://www.google.com/search?q=nba+allstars+2026",
      "render": true
  }'
  ```
</CodeGroup>

<Warning>
  When `render: false` (default), the API returns the raw HTML without
  JavaScript execution, suitable for static pages.
</Warning>

### Automatic driver selection

Set `render: "auto"` to let Nimble pick the optimal configuration per domain. The engine starts cheap and fast, and escalates to rendering and stealth only when needed:

<CodeGroup>
  ```python Python theme={"system"}
  from nimble_python import Nimble

  nimble = Nimble(api_key="YOUR-API-KEY")

  result = nimble.extract.run(
      url="https://www.example.com",
      render="auto"
  )

  print(result.data.html)
  ```

  ```typescript TypeScript theme={"system"}
  import Nimble from "@nimble-way/nimble-js";

  const nimble = new Nimble({ apiKey: "YOUR-API-KEY" });

  const result = await nimble.extract.run({
    url: "https://www.example.com",
    render: "auto",
  });

  console.log(result.data?.html);
  ```

  ```bash cURL theme={"system"}
  curl -X POST 'https://sdk.nimbleway.com/v2/extract' \
  --header 'Authorization: Bearer <YOUR-API-KEY>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "url": "https://www.example.com",
      "render": "auto"
  }'
  ```
</CodeGroup>

To control how the engine escalates, provide `auto_driver_configuration` with the configurations to try and how many attempts to spend on each:

<CodeGroup>
  ```python Python theme={"system"}
  from nimble_python import Nimble

  nimble = Nimble(api_key="YOUR-API-KEY")

  # Try a plain HTTP request once, then headless rendering,
  # then maximum stealth
  result = nimble.extract.run(
      url="https://www.example.com",
      auto_driver_configuration={
          "vx6-fast": 1,
          "vx8": 3,
          "vx10-pro": 2
      }
  )

  print(result.data.html)
  ```

  ```typescript TypeScript theme={"system"}
  import Nimble from "@nimble-way/nimble-js";

  const nimble = new Nimble({ apiKey: "YOUR-API-KEY" });

  // Try a plain HTTP request once, then headless rendering,
  // then maximum stealth
  const result = await nimble.extract.run({
    url: "https://www.example.com",
    auto_driver_configuration: {
      "vx6-fast": 1,
      vx8: 3,
      "vx10-pro": 2,
    },
  });

  console.log(result.data?.html);
  ```

  ```bash cURL theme={"system"}
  curl -X POST 'https://sdk.nimbleway.com/v2/extract' \
  --header 'Authorization: Bearer <YOUR-API-KEY>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "url": "https://www.example.com",
      "auto_driver_configuration": {
        "vx6-fast": 1,
        "vx8": 3,
        "vx10-pro": 2
      }
  }'
  ```
</CodeGroup>

<Note>
  `auto_driver_configuration` implies `render: "auto"`, so you don't need to
  set both. If you also set a specific `driver`, the driver wins and
  `auto_driver_configuration` is ignored.
</Note>

### Dynamic content loading

Wait for AJAX-loaded content with a `wait_for_navigation` browser action:

<CodeGroup>
  ```python Python theme={"system"}
  from nimble_python import Nimble

  nimble = Nimble(api_key="YOUR-API-KEY")

  # Wait for all network activity to stop
  result = nimble.extract.run(
      url="https://www.target.com/deals/all",
      render=True,
      browser_actions=[
          {"wait_for_navigation": "networkidle0"}
      ]
  )

  print(result.data.html)
  ```

  ```typescript TypeScript theme={"system"}
  import Nimble from "@nimble-way/nimble-js";

  const nimble = new Nimble({ apiKey: "YOUR-API-KEY" });

  // Wait for all network activity to stop
  const result = await nimble.extract.run({
    url: "https://www.target.com/deals/all",
    render: true,
    browser_actions: [{ wait_for_navigation: "networkidle0" }],
  });

  console.log(result.data?.html);
  ```

  ```bash cURL theme={"system"}
  curl -X POST 'https://sdk.nimbleway.com/v2/extract' \
  --header 'Authorization: Bearer <YOUR-API-KEY>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "url": "https://www.target.com/deals/all",
      "render": true,
      "browser_actions": [
        { "wait_for_navigation": "networkidle0" }
      ]
  }'
  ```
</CodeGroup>

To wait for a specific element instead — say a product grid rendered after an API call — use `wait_for_element`:

```json theme={"system"}
"browser_actions": [
  { "wait_for_element": ".product-grid" }
]
```

### Combining with browser actions

Rendering works seamlessly with the full [browser actions](/nimble-sdk/web-tools/extract/features/browser-actions) toolkit — click, scroll, fill, and wait in sequence:

<CodeGroup>
  ```python Python theme={"system"}
  from nimble_python import Nimble

  nimble = Nimble(api_key="YOUR-API-KEY")

  # Render page, then perform actions
  result = nimble.extract.run(
      url="https://www.example.com",
      render=True,
      browser_actions=[
          {
              "click": {
                  "selector": "button.load-more",
                  "timeout": 5000
              }
          },
          {
              "wait": "2s"
          },
          {
              "auto_scroll": 5000
          }
      ]
  )

  print(result.data.html)
  ```

  ```typescript TypeScript theme={"system"}
  import Nimble from "@nimble-way/nimble-js";

  const nimble = new Nimble({ apiKey: "YOUR-API-KEY" });

  // Render page, then perform actions
  const result = await nimble.extract.run({
    url: "https://www.example.com",
    render: true,
    browser_actions: [
      {
        click: {
          selector: "button.load-more",
          timeout: 5000,
        },
      },
      {
        wait: "2s",
      },
      {
        auto_scroll: 5000,
      },
    ],
  });

  console.log(result.data?.html);
  ```

  ```bash cURL theme={"system"}
  curl -X POST 'https://sdk.nimbleway.com/v2/extract' \
  --header 'Authorization: Bearer <YOUR-API-KEY>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "url": "https://www.example.com",
      "render": true,
      "browser_actions": [
        {
          "click": {
            "selector": "button.load-more",
            "timeout": 5000
          }
        },
        {
          "wait": "2s"
        },
        {
          "auto_scroll": 5000
        }
      ]
  }'
  ```
</CodeGroup>

## Best practices

### Choose the right load condition

* **`load`** — default for most websites; waits for the standard page load event.
* **`domcontentloaded`** — fastest; use when images and styles aren't needed.
* **`networkidle2`** — AJAX-heavy pages; waits until most requests finish.
* **`networkidle0`** — most thorough; use when you need everything loaded.

```json theme={"system"}
"browser_actions": [
  { "wait_for_navigation": "networkidle2" }
]
```

### Combine with network capture

Monitor and capture API calls during rendering:

```python theme={"system"}
from nimble_python import Nimble

nimble = Nimble(api_key="YOUR-API-KEY")

# Render page and capture specific API calls
result = nimble.extract.run(
    url="https://www.example.com",
    render=True,
    network_capture=[
        {
            "method": "GET",
            "url": {
                "type": "contains",
                "value": "/api/products"
            }
        }
    ]
)

# Access captured network data
api_responses = result.data.network_capture
print(api_responses)
```

### Performance optimization

**Choose the minimal driver:**

```python theme={"system"}
# ❌ Don't use vx10 when vx8 works
result = nimble.extract.run(
    url="https://simple-spa.com",
    driver="vx10",  # Unnecessary stealth features
    render=True
)

# ✅ Use appropriate driver
result = nimble.extract.run(
    url="https://simple-spa.com",
    driver="vx8",  # Sufficient for most SPAs
    render=True
)
```

Or let `render: "auto"` pick for you — it escalates only as far as the target domain requires.

### When to skip rendering

Use non-rendering (vx6) when:

* **Static content**: HTML already contains all data
* **API endpoints**: Fetching JSON directly
* **High throughput needed**: Rendering is slower
* **Simple pages**: No JavaScript required
* **Cost optimization**: Non-rendering is cheaper

```python theme={"system"}
# ✅ Skip rendering for static pages
result = nimble.extract.run(
    url="https://static-site.com/page.html",
    render=False,  # or omit (false is default)
)
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.