Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>DuckDB sidekickNew to Visual Studio Code? Get it now.
DuckDB sidekick

DuckDB sidekick

PrasertKana

|
61 installs
| (0) | Free
Read-only DuckDB, Parquet, CSV, TSV, PSV, and Excel viewer with SQL history and export to CSV, Excel, DuckDB, and Jupyter
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

DuckDB sidekick

A read-only DuckDB, Parquet, delimited-text, and Excel data viewer for Visual Studio Code.

Open a .duckdb, .parquet, or .parq file normally. For .csv, .tsv, .tab, .psv, or .xlsx, right-click the file in the Explorer and choose Open with DuckDB Sidekick, run PK DuckDB: Query Tabular File with DuckDB, or use Open With > Query Tabular File with DuckDB. The tabular editor is optional so it does not replace an installed CSV or Excel viewer.

Features

  • Explorer panel listing databases, schemas, tables, views, and their columns.
  • DuckDB tables and views appear as workbook-style tabs.
  • SQL suggestions for keywords, types, DuckDB functions, tables, views, and columns, aware of the clause and of FROM aliases.
  • Parquet files appear as one logical table.
  • CSV, TSV, TAB, and PSV files are exposed to SQL as data.
  • Excel sheets appear as workbook-style tabs and keep their sheet names as SQL relation names. Sheets that aren't tables — a chart sheet, a pivot-only sheet, an empty one — are skipped so one bad tab can't stop the workbook opening.
  • Virtualized rows and columns for large datasets.
  • Column names and DuckDB logical types remain visible while scrolling.
  • Repeating 16-color, color-blind-friendly column palette.
  • A Theme toolbar button forces a colorblind-friendly light or dark theme, independent of the VS Code theme; the choice is one setting shared by every open viewer and survives a reload.
  • Float columns show one consistent number of decimals so the decimal points line up, without the noise digits binary floating point prints.
  • A compact comma toggle adds thousands separators without converting exact integers to JavaScript numbers.
  • On-demand, wrapping data-profile cards with key statistics, missing and approximate unique counts, distributions, and complete PNG download.
  • Seven interactive charts — distribution, relationship, comparison, part-to-whole, correlation, trend, and pivot — with global filters. Every chart is aggregated by DuckDB, so it opens a huge file as cheaply as a small one.
  • Sortable columns, Excel-style cell/range/row/column selection and keyboard navigation, Tail 10, selection-aware copy, Focus Cell, resizing, and auto-fit.
  • Grid right-click menus provide Copy Cell and Copy Column in regular and frozen panes.
  • Excel-style freeze panes pin rows and columns while the rest of the grid scrolls.
  • Session SQL editor with Ctrl/Cmd+Enter execution and cancellation, and syntax highlighting for keywords, types, strings, quoted identifiers, numbers, function calls, and comments.
  • One-click Prettify reformats the query into a consistent clause-per-line layout with indented subqueries.
  • Read-only SQL guard blocks mutating and multi-statement commands.
  • Typed query results appear in the same colored grid.
  • SQL history remains in memory for the current open-document session.
  • Selected history exports to a Python/JupySQL .ipynb notebook.
  • Every exported SQL cell starts with %%sql followed by a newline.
  • Active tables and the active SQL query result export as CSV, a typed JSON array, a Markdown table, Excel, or a DuckDB database. One Export… button opens a panel that picks the columns, format, and options. A query result export reruns the query so the file holds every row, not only the rows shown under the display limit.
  • CSV is written as UTF-8, with an optional byte order mark for double-clicking into Excel on Windows; Excel exports are UTF-8 by construction.
  • A DuckDB export keeps column types instead of flattening them to text, and several exports accumulate in one file.
  • Open files reload after external changes.
  • Explorer right-click entry for opening a data file in the viewer.

Source files are never modified.

Explorer context menu

Right-clicking a .csv, .tsv, .tab, .psv, or .xlsx file in the VS Code Explorer adds Open with DuckDB Sidekick, which opens it in the read-only viewer, the same as Open With > Query Tabular File with DuckDB. It acts on the whole selection, so selecting several files and choosing the entry opens all of them.

Remote repositories

The viewer supports files opened through VS Code Remote - SSH, WSL, Dev Containers, Codespaces, and desktop virtual workspaces such as Remote Repositories. Install the extension in the location offered by VS Code, then open the data file normally or use Open With.

For a virtual repository, the extension reads the resource through VS Code and opens a disposable local snapshot with DuckDB. The snapshot is refreshed when the source changes and removed when the viewer closes. Read-only repositories still support viewing, SQL, profiles, and CSV or DuckDB export to a separate writable location.

Notebook export requires a local source because the exported notebook must reopen the data file outside VS Code. The browser-only editors at vscode.dev and github.dev are not supported because this extension uses DuckDB's native Node.js library.

Grid interaction

Click a column header to sort it. Repeated clicks cycle through ascending, descending, and the relation's original browsing order; ▲ or ▼ beside the column name shows the active direction. Each table or query result remembers its own sort while you switch tabs. Table pages are sorted by DuckDB, with rowid as a secondary key for stable paging on physical tables. SQL query results are sorted in the viewer from an untouched snapshot, so the third click can restore their original row order exactly.

Clicking a header also selects it. Shift+click selects a contiguous header range, Ctrl/Cmd+click toggles one row or column, and Ctrl/Cmd+Shift+click adds a range without changing the sort. Right-click selects without sorting. The top-left corner or Ctrl/Cmd+A selects all displayed cells; Escape or an empty grid click clears the selection.

Focus the grid and use arrows, Home, End, Page Up, Page Down, Tab, Enter, and their Shift or Ctrl/Cmd variants for Excel-style movement and range extension. Ctrl/Cmd+Space selects the active column, Shift+Space the active row, and both modifiers select all cells. After selecting a whole row or column, Shift+arrow extends or shrinks that selection along its axis; add Ctrl/Cmd to jump to the edge. Tail 10 switches to the final ten rows in the current sort order; Show all restores the full relation.

Ctrl/Cmd+C copies a rectangular cell range or one or more selected rows or columns as TSV. Whole columns include their headers and all rows, including uncached table pages. Ctrl/Cmd+Shift+C prepends column headers to selected rows. Tabs, line breaks, and quotes are escaped, while nulls remain empty and values keep their exact raw text rather than display-only formatting.

The right-click menu labels the active operation as Copy Cell, Cells, Row(s), Column(s), or All Cells. Right-clicking the top-left corner selects the entire displayed grid and opens that menu. The same selection and copy behavior works in frozen panes. Right-clicking inside the SQL editor keeps its editor menu.

Drag the right edge of a header to resize that column, down to a minimum of 40px. Double-click the edge to fit that column to its widest cached or displayed value, capped at 400px. Auto-fit all applies the same measurement to every column.

Focus Cell highlights the active row and column, including their headers. While it is on, Column colors is turned off and disabled so the two background treatments do not compete. Leaving Focus Cell restores Column colors to exactly the setting it had before, whether that setting was on or off.

Freeze panes

Select a cell and click Freeze panes. Everything above and to the left of it stays put while the rest of the grid scrolls, exactly as in Excel — so selecting the cell in row 3, column C pins the first two rows and the first two columns. The button then reads Unfreeze and removes the split; to move a split, unfreeze, select a new cell, and freeze again. Both actions are also available from the command palette as PK DuckDB: Freeze Panes at Active Cell and PK DuckDB: Unfreeze Panes.

Selecting the very first cell freezes nothing, since there is nothing above or to its left. A split always leaves at least one row and one column free, so the grid can never be pinned solid.

Each relation remembers its own split, so switching tabs and coming back keeps it. The split is never written to the source file, and it is dropped when the file changes on disk, because a relation's shape may have changed with it.

One difference from Excel: columns you scroll past slide underneath the frozen ones rather than stopping beside them. Freezing the leftmost columns to keep an identifier in view works as expected; freezing many columns and then scrolling far right will hide some behind the frozen block.

Theme

The Theme toolbar button cycles the viewer through three states:

State What it does
Theme: VS Code Follow the current VS Code color theme (the default).
Theme: Light Force a colorblind-friendly light theme, whatever VS Code is using.
Theme: Dark Force a colorblind-friendly dark theme.

The two forced themes are built from the Okabe-Ito colorblind-safe palette, so nothing in the chrome — SQL syntax highlighting, the correlation chart, the explorer tree, status marks — rests on a red/green distinction. Text stays at WCAG AA contrast and other marks at 3:1.

The choice is stored in the pkDuckDb.theme setting, not per file: it is a vision preference, so every open viewer follows it, a new file opens with it, and it survives a window reload. Changing the setting directly, or running PK DuckDB: Switch Viewer Theme from the command palette (works with no viewer focused), has the same effect.

Number display

Numeric columns are right-aligned in the grid, and DATE, TIME, and TIMESTAMP columns are right-aligned with them: they read as ordered quantities rather than as prose, so their digits line up down the column. Everything else stays left-aligned.

FLOAT and DOUBLE columns are formatted for reading. Every row in a column gets the same number of decimals, so the decimal points line up:

Stored value Shown
1499.95, 20, 3.5 1499.95, 20.00, 3.50
0.30000000000000004 0.3
0.3333333432674408 (a FLOAT) 0.3333
0.0000012345678 0.000001235

Two different things are being removed. A FLOAT holds about 7 real digits but is printed with 16, and 0.1 + 0.2 famously prints as 0.30000000000000004 — those trailing digits are an artifact of binary floating point, not your data, and are always dropped. Genuine precision beyond that is capped by pkDuckDb.maxDecimalDigits, which defaults to 4.

A column whose values are too small for the cap widens instead of rounding to zero, and scientific notation is never used.

The comma button in the toolbar toggles thousands separators for numeric cells:

Raw value Shown with separators
1234567.50 1,234,567.50
9007199254740993 9,007,199,254,740,993

Grouping is performed directly on the value's text, so integers larger than JavaScript's safe-number range keep every digit. The separator pass leaves exponent notation and non-numeric text unchanged.

DECIMAL columns are left exactly as DuckDB renders them, at the scale your schema declares — DECIMAL(6,3) keeps its three places. Integers, including HUGEINT, never pass through the floating-point formatter.

All number formatting is display only. Ctrl/Cmd+C and the grid context menu copy exact stored values, hovering a cell shows the raw value, and CSV, Excel, and DuckDB exports are unaffected.

Explorer

Click Explorer in the viewer toolbar to show or hide the tree of databases, schemas, tables, views, and columns. Selecting a table or view opens it in the grid and highlights its bottom tab, so the tree and the tabs stay in step.

The tree is read from DuckDB's catalog, so expanding a table never scans it. Columns for a Parquet, delimited, or Excel source are read when the relation is first expanded, which keeps opening a many-sheet workbook fast.

Click Data profile in the viewer toolbar to open a resizable pane beside the grid. It has three tabs:

  • Record — the selected row read down the page, one field per line, with a stepper to move between rows. Clicking a field selects that column in the grid.
  • Column — a full-height profile card for the active cell's column.
  • All columns — a responsive grid of one card per column. Clicking a card selects that column in the grid.

Profiles are calculated lazily by DuckDB and cached for each relation until the source file changes. The Query Result tab is also profileable: its successful SELECT is rerun once to calculate statistics over the full result rather than only the displayed row limit. PNG saves the current tab as a theme-aware image (or a transparent-background one).

Charts

Click Chart in the viewer toolbar, or run PK DuckDB: Toggle Chart, to chart the active table. Ten views are available:

View What it answers
Distribution How are one column's values spread? Histogram, box plot, or Pareto.
Relationship Do two numeric columns move together? Scatter.
Comparison Which categories are biggest? Bar, upright or turned on its side.
Bullet Did each group hit the target it was set? One row per group.
Part-to-Whole How does each category divide? Stacked or 100% bar.
Range How wide is each group's spread? A dot at each end of its range.
Correlation Which numeric columns relate? Pearson heatmap.
Trend How does a measure move over time? Line or area.
Facets Does the shape hold in every slice? A grid of small charts on shared scales.
Pivot Cross-tabulate two dimensions. Bar, heatmap, and a pivot table.

Four filter slots above the controls narrow every view at once. Pick a column and either tick the values you want or type a range.

All aggregation happens inside DuckDB and only the finished series is sent to the chart, so charting does not load your data into memory. Where a view would otherwise produce an unreadable chart it is bounded and says so: the top categories are kept, and a scatter plots a random sample whose size is reported next to the chart. The caps are configurable through the pkDuckDb.lens* settings.

A trend shows every interval between its first and its last, including the ones nothing happened in, so a quiet month is a gap in the line rather than a month the axis skips. When a trend or a scatter carries more points than fit across the panel, a slider appears under it: drag the slider, or scroll and drag on the chart itself, to narrow the range. Changing any control resets it.

Reference lines

Distribution takes any of Mean, Median and Quartiles at once — comparing them is the point, and a mean pulled away from a median is the whole story of a skew. Mean and median are on by default; the quartiles are left to be asked for. Because the median is the 50th percentile, asking for both still draws one median rather than stacking two lines on a spot.

Comparison draws its line across the bars instead, at their mean, their median, both, or a number you type — off by default, a bar chart being read against its own bars. A box plot is offered none: it already draws its median, and its axis is the values themselves. A Pareto is offered none for the opposite reason: its axis is its categories, so a line down it would stand at no value at all. Both withdraw the control rather than showing it to no effect.

Range marks the same three statistics on each row's own line, where they answer a different question — where a group sits inside its own spread, rather than where the whole chart's middle is. A quartile mark is withdrawn when the dots are already at Q1 and Q3, since it would be drawn on top of itself.

Each line is told apart three ways — a colourblind-safe colour, a dash, and a weight — so four crossing one chart stay distinguishable to a reader who cannot separate the hues, and under a forced palette that drops colour entirely.

Pareto, Bullet, Range and Facets

A Pareto ranks a column's total by category, largest first, with the running share of the whole climbing over it up a right-hand percentage axis and a fixed guide at 80%. Where the curve crosses the guide is how few categories account for most of the number. The shares are divided by the total over every category, including the ones the cap dropped, so a capped chart's last bar reads below 100% rather than claiming the cap was the whole.

A Bullet gives each row a target of its own instead of one line across them all: the target is a wide quartered bar, the actual a narrower one centred inside it, and a green tick stands at the goal so it stays readable when the actual overshoots. Both columns are reduced by the same aggregate. Show as switches between the measured values and each row's share of its own target, which is what makes a small group that beat its quota comparable to a large one that missed.

A Range draws one row per group with a dot at each end of its span — min–max, or the middle half between Q1 and Q3, which is what a single outlier stops min–max from showing. Unlike a box plot a single-value group is kept: a range of one value is a real answer, and it draws as one dot.

Facets answers whether a shape holds in every slice without setting a filter, reading, changing it, and holding the last one in your head: the same chart drawn once per slice. Give it a value and a facet row, a facet column, or both. Nothing a cell draws is decided by that cell — the bins are measured once over the whole column, the categories are ranked once over every row, and every cell is given the same ceiling, so a taller bar means more wherever it is. An empty slice still gets its cell, because a gap in the grid is a fact about the data. A date is coarsened rather than truncated, through the Every control; anything else keeps the largest slices and gathers the rest into one quieter cell that says how many it stands for. The grid scrolls rather than squeezing its rows.

A column whose name reads as a rate (conversion_rate, pct_churn, margin) and whose values sit in −1…1 is charted as a percentage: its axes and histogram edges read 0.0425 as "4.25%", and so does a mean, median, min or max of it — but not a sum or a count, which are no longer rates.

Every chart also publishes its numbers as a table under Show data, which can be copied as TSV, and Save image writes the chart itself as a PNG next to your file. Under a high-contrast theme the table replaces the chart, since a canvas cannot follow a forced palette.

SQL and notebook export

Click SQL in the viewer toolbar, enter one read-only query, and select Run or press Ctrl/Cmd+Enter. The editor shows line numbers, and the panel is resized by dragging its bottom edge.

The viewer accepts result-producing statements such as SELECT and EXPLAIN. Statements such as INSERT, UPDATE, DELETE, CREATE, COPY, ATTACH, INSTALL, and LOAD are blocked.

Suggestions appear as you type and can be requested with Ctrl+Space. What is offered first depends on the clause: relations after FROM and JOIN, columns after SELECT, WHERE, ON, GROUP BY, and ORDER BY, and keywords at the start of a statement. Typing alias. narrows to that relation's columns, and schema. or database. walks down the catalog. Use ↑ and ↓ to move, Enter or Tab to accept, and Escape to dismiss; Ctrl/Cmd+Enter still runs the query. Names that need quoting are inserted quoted. Set pkDuckDb.sqlSuggestions to false to turn the popup off.

Prettify

Prettify in the SQL panel, Shift+Alt+F in the editor, right-click in the editor, or PK DuckDB: Prettify SQL reformats the query in place:

SELECT prefs.purchase_purpose,
  prefs.preference,
  prefs.rating
FROM (
  SELECT b.purchase_purpose,
    p.preference,
    p.rating,
    RANK() OVER (PARTITION BY b.purchase_purpose ORDER BY p.rating DESC) AS rank
  FROM buyers AS b
  JOIN prefs AS p
  ON b.buyer_id = p.buyer_id
) AS prefs
WHERE prefs.rank <= 5
ORDER BY prefs.purchase_purpose,
  prefs.rating DESC;

Each clause starts a line. The first item stays on the clause's line and later items — separated by a comma, or by AND or OR — wrap to one extra level of indentation. A subquery's parentheses open an indent level and its closing parenthesis returns to the clause indent, so ) AS prefs reads as one line. Parentheses holding an expression never break, which keeps a window specification such as OVER (PARTITION BY x ORDER BY y) intact.

Keywords and type names are uppercased. Identifiers, quoted identifiers, string literals, function names, and comments keep the spelling you gave them, and the caret stays with the token it was on.

Right-clicking the SQL editor opens a menu with Prettify SQL and Run SQL alongside Cut, Copy, Paste, and Select All. Each entry shows its keyboard shortcut, which remains available if your platform refuses clipboard access to the viewer. The selection is captured when the menu opens, so Ctrl/Cmd+C still copies that selection even if opening the menu moved the caret.

The History panel records each command, status, duration, and displayed row count until the document closes. Export Notebook creates a JupySQL notebook that reconnects to the opened DuckDB database in read-only mode. Parquet exports create an in-memory DuckDB connection and register the file as a view. Delimited and Excel exports recreate the same source relations and parsing options.

Export

With one or more column headers selected, Export selected ▾ offers CSV, TSV, PSV, and a typed JSON array. These quick exports contain every row in the current sort order and only the selected columns. Delimited formats include a header and UTF-8 BOM; JSON remains typed and has no BOM. The quick menu requires non-empty, unique column names. Query-result quick exports rerun the successful SELECT so they are not limited to displayed rows.

Export… in the viewer toolbar opens a panel over whatever the grid is showing — the active table, or the active SQL query result. A query result export reruns the query, so the file holds every row rather than the display-limited page.

The panel chooses:

  • Which columns. Every column starts checked. Filter the list by name, use All / None / Invert on whatever the filter left showing, and shift-click to check or uncheck a run of columns at once. Each column carries the colour it has in the grid and its DuckDB type. Leaving everything checked exports everything, and the export then keeps a plain SELECT * rather than a column list, so nothing gets reordered or renamed on the way out.
  • Which format — CSV, JSON Array, Markdown table, Excel, or DuckDB.
  • Include header row, for CSV and Excel. JSON has no header, Markdown requires one, and a DuckDB table has no header row, so the option is dimmed for those formats.
  • UTF-8 BOM, for CSV, off by default. Dimmed for every other format.

Column picks last while you stay on the same table, and reset when you switch to another one.

The file being viewed can never be the destination, whichever format you pick. It stays read-only.

Each format is also a command, which opens the same panel with that format already chosen: Export Active Table or Query Result as CSV, … as Excel, and … to DuckDB.

CSV

Written as plain UTF-8. Check UTF-8 BOM to lead the file with a byte order mark instead.

The mark matters when you intend to double-click the file: without one, Excel on Windows decodes a CSV with the ANSI code page, which turns Thai, Japanese, and accented text into mojibake. It is off by default because most other tools — pandas, duckdb, git, a text editor — would rather see the bare bytes, and Excel's own Data → From Text import reads UTF-8 correctly without it.

JSON Array

Written as a typed JSON array of row objects. Numbers and booleans remain JSON values rather than being flattened to display text, and the file has no BOM.

Markdown table

Written as a GitHub-flavoured Markdown table. Pipes, backslashes, and line breaks in values are escaped so they cannot change the table shape. Every numeric, DATE, TIME, and TIMESTAMP DuckDB column uses a trailing-colon alignment marker (---:), making the whole column right-aligned even when its values are empty or NULL, matching how the grid aligns the same columns.

Excel

Written as a single-sheet .xlsx workbook, named after the table or query it came from. Excel's own rules for a sheet name are applied to that name — 31 characters, and none of : \ / ? * [ ] — so the workbook always opens. The format is UTF-8 throughout, so text needs no special handling.

Excel stores no column types, so this is the format to pick when the numbers matter more than the types. It also caps a sheet at 1,048,576 rows; a larger export reports that rather than truncating.

DuckDB

Written into a DuckDB database as a table you name. Column types survive, so a DATE stays a DATE and a DECIMAL keeps its scale.

Choosing a database that already exists adds the table to it and keeps the tables already there, which is how several exports collect into one file. Your operating system's save dialog still asks whether to replace the file; the export never does. The one case that overwrites is a name already used inside that database, and it asks first.

Excel is the one source whose types need help here. The format has no integer type — every numeric cell is a floating-point number — so a column of whole numbers would otherwise be stored as DOUBLE. Exporting a sheet checks each numeric column and narrows the ones holding only whole numbers to BIGINT, leaving genuinely fractional columns as DOUBLE. This applies to exporting a sheet; exporting a query result over a sheet keeps the types the query produced, since a computed value such as an average is a real DOUBLE even when it lands on a whole number.

Tabular source loading

The viewer uses a scan-then-materialize strategy:

  • Auto materializes delimited files up to 128 MB in memory, uses a temporary on-disk DuckDB cache for larger files, and uses disk for Excel.
  • Memory and Disk force materialization to the selected session store.
  • Direct leaves relations as file scans, which opens quickly but rescans the source for repeated queries and profiles.

The cache lives in the operating system temporary directory and is removed when the viewer worker closes. Excel sheets are registered immediately and materialized individually when first browsed, profiled, or exported. SQL can query any registered sheet before materialization.

Trailing delimiters

Several systems write a delimited file with the delimiter at the end of every line, header included — id,name,updated,. That gives each row one more field than the file has named columns, and DuckDB reads the extra one as a column it names itself, with nothing in it.

The viewer drops that column, so the grid, the profile, and every export show the file as it was meant. It is dropped only when both signs agree: the header cell was blank, and the column holds no value in any row. A column the file named is always kept, and so is an unnamed one that turns out to carry data — this can hide a column, but never loses a value. Setting Header to no in Source settings keeps every column, since a file read without a header has no named columns to compare against.

Use Source settings in the viewer toolbar to change storage, delimiter, header, encoding, type inference, malformed-row handling, or Excel empty-row behavior for the current open session. Workspace defaults are available under pkDuckDb.tabular* settings. TSV/TAB defaults to tab, PSV defaults to pipe, and Excel treats the first row as a header unless No header row is selected.

Commands

  • PK DuckDB: Toggle Explorer
  • PK DuckDB: Toggle SQL Editor
  • PK DuckDB: Run SQL
  • PK DuckDB: Cancel SQL
  • PK DuckDB: Prettify SQL
  • PK DuckDB: Show SQL History
  • PK DuckDB: Export SQL History as Jupyter Notebook
  • PK DuckDB: Clear SQL History
  • PK DuckDB: Export Active Table or Query Result as CSV
  • PK DuckDB: Export Active Table or Query Result as Excel
  • PK DuckDB: Export Active Table or Query Result to DuckDB
  • PK DuckDB: Auto-fit All Columns
  • PK DuckDB: Freeze Panes at Active Cell
  • PK DuckDB: Unfreeze Panes
  • PK DuckDB: Query Tabular File with DuckDB
  • PK DuckDB: Configure Tabular Source
  • PK DuckDB: Switch Viewer Theme (VS Code, Light, Dark)

Open with DuckDB Sidekick is an Explorer right-click entry rather than a palette command, because it acts on the file you clicked.

Contributing

Building the extension from source, running tests, and packaging a release are covered in DEV.md.

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft