DuckDB sidekickA read-only DuckDB, Parquet, delimited-text, and Excel data viewer for Visual Studio Code. Open a Features
Source files are never modified. Explorer context menuRight-clicking a Remote repositoriesThe 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 Grid interactionClick 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
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 panesSelect 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. ThemeThe Theme toolbar button cycles the viewer through three states:
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 Number displayNumeric columns are right-aligned in the grid, and
Two different things are being removed. A 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:
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.
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. ExplorerClick 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:
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
ChartsClick Chart in the viewer toolbar, or run PK DuckDB: Toggle Chart, to chart the active table. Ten views are available:
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
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 linesDistribution 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 FacetsA 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 ( 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 exportClick 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 Suggestions appear as you type and can be requested with Ctrl+Space. What is
offered first depends on the clause: relations after PrettifyPrettify 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:
Each clause starts a line. The first item stays on the clause's line and later
items — separated by a comma, or by 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. ExportWith 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:
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. CSVWritten 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 — JSON ArrayWritten 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 tableWritten as a GitHub-flavoured Markdown table. Pipes, backslashes, and line
breaks in values are escaped so they cannot change the table shape. Every
numeric, ExcelWritten as a single-sheet 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. DuckDBWritten into a DuckDB database as a table you name. Column types survive, so a
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 Tabular source loadingThe viewer uses a scan-then-materialize strategy:
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 delimitersSeveral systems write a delimited file with the delimiter at the end of every
line, header included — 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 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
Commands
Open with DuckDB Sidekick is an Explorer right-click entry rather than a palette command, because it acts on the file you clicked. ContributingBuilding the extension from source, running tests, and packaging a release are covered in DEV.md. LicenseMIT |