---
sidebar_position: 1
title: useSQLQuery hook
description: React hook for querying MotherDuck data from within Dives.
feature_stage: preview
---

The `useSQLQuery` hook is a React hook that runs SQL queries against MotherDuck from within a [Dive](/key-tasks/ai-and-motherduck/dives). It handles loading states and error reporting so your component can focus on rendering data.

## Import

```jsx
import { useSQLQuery } from "@motherduck/react-sql-query";
```

## Syntax

```jsx
const { data, isLoading, isError, error } = useSQLQuery(sql, options);
```

## Parameters

### `sql`

| Type | Required |
|------|----------|
| `string` | Yes |

A SQL query string to run against MotherDuck. The query runs server-side and returns rows as objects.

Use fully qualified, double-quoted table names in your queries (`"database"."schema"."table"`) to avoid issues when the Dive runs outside your current database context.

### `options`

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `enabled` | `boolean` | `true` | Set to `false` to skip query execution. Useful when a query depends on user input that isn't available yet. |

## Return value

| Property | Type | Description |
|----------|------|-------------|
| `data` | array or `undefined` | Query result as an array of row objects. `undefined` while loading. |
| `isLoading` | `boolean` | `true` while the query is running |
| `isError` | `boolean` | `true` if the query failed |
| `error` | `Error` or `null` | Error object if the query failed |

:::warning
`data` is the row array directly — there is no `data.rows` wrapper. Always guard against `undefined`:

```jsx
const rows = Array.isArray(data) ? data : [];
```

:::

## Numeric values

DuckDB returns `BIGINT`, `HUGEINT`, and `DECIMAL` as JavaScript `BigInt` or special objects, not `number`. These crash if rendered in JSX or used with `.toFixed()`. Define this helper at the top of every Dive and wrap all numeric values:

```jsx
const N = (v) => (v != null ? Number(v) : 0);
```

## Examples

### Basic query

```jsx
import { useSQLQuery } from "@motherduck/react-sql-query";

const N = (v) => (v != null ? Number(v) : 0);

export default function Dive() {
  const { data, isLoading } = useSQLQuery(`
    SELECT category, SUM(amount) AS total
    FROM "my_db"."main"."sales"
    GROUP BY ALL
  `);

  if (isLoading) return <div>Loading...</div>;

  const rows = Array.isArray(data) ? data : [];

  return (
    <ul>
      {rows.map((row) => (
        <li key={row.category}>
          {row.category}: ${N(row.total).toLocaleString()}
        </li>
      ))}
    </ul>
  );
}
```

### Conditional queries with `enabled`

Skip a query until a user selection is available:

```jsx
const [selected, setSelected] = useState(null);

const { data: details } = useSQLQuery(`
  SELECT * FROM "my_db"."main"."products"
  WHERE category = '${selected}'
`, { enabled: !!selected });
```

### Multiple independent queries

Each `useSQLQuery` call loads independently. Render the page layout immediately and show inline placeholders per section instead of a single loading spinner:

```jsx
const summary = useSQLQuery(`
  SELECT COUNT(*) AS total, SUM(revenue) AS revenue
  FROM "my_db"."main"."orders"
`);

const monthly = useSQLQuery(`
  SELECT strftime(date_trunc('month', order_date), '%Y-%m') AS month,
         SUM(revenue) AS revenue
  FROM "my_db"."main"."orders"
  GROUP BY 1 ORDER BY 1
`);

return (
  <div>
    {summary.isLoading
      ? <div className="h-12 w-24 bg-gray-200 animate-pulse rounded" />
      : <p>{N(summary.data?.[0]?.revenue)}</p>
    }
    {monthly.isLoading
      ? <div className="bg-gray-100 animate-pulse rounded" style={{height: 250}} />
      : <MyChart data={monthly.data} />
    }
  </div>
);
```

## Tips

- **Format dates in SQL, not JavaScript.** Use DuckDB's `strftime()` to format dates and timestamps as strings. DuckDB date types are returned as special objects that don't render in JSX.
- **Fill time series gaps in SQL.** Recharts does not interpolate missing time periods. Use `generate_series` with a `LEFT JOIN` to produce a continuous date spine.
- **Dollar-quoted strings.** When passing Dive content through SQL (for example, with [`MD_CREATE_DIVE`](../md-create-dive)), use [dollar-quoted string literals](https://duckdb.org/docs/stable/sql/data_types/literal_types#dollar-quoted-string-literals) to avoid escaping issues with nested quotes.

## Related

- [Dives SQL functions](/sql-reference/motherduck-sql-reference/ai-functions/dives/) — Manage Dives with SQL
- [Creating visualizations with Dives](/key-tasks/ai-and-motherduck/dives) — How-to guide
- [MCP Server](/sql-reference/mcp/) — Create Dives through AI assistants
