Open-source project
visprex/visprex avatar
visprex/visprex

Visprex: a browser-only CSV explorer with histograms, regressions and a correlation map

Visualise your CSV files in seconds without sending your data anywhere

527 stars12 forksTypeScriptMIT

At a glance

What is it?
Visprex is a TypeScript single page application for loading a CSV, inferring column types, and exploring distributions, relationships and regressions, and the README's central claim is that the data is processed entirely in the browser with no backend, the only network calls being downloads of example datasets. It ships as a Vite and React 19 build served by a hand-compiled nginx image.
Who is it for?
Adopt Visprex if you need to look at a CSV quickly and the data cannot leave your machine, since the README states that the processing happens entirely in the browser and that the only network calls are for example datasets, and if you want the regression output with R-squared, standard errors, confidence intervals and p-values rather than a picture.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The claim that shapes the whole tool

The repository description is the pitch: visualise your CSV files in seconds without sending your data anywhere. The homepage is visprex.com and the documentation is at docs.visprex.com.

That claim is a design constraint rather than a marketing line, and the README backs it with a specific statement about the mechanism. Your data is processed entirely in your browser without sending your data to any backend servers, and the only network calls this application makes are for downloading example datasets from public GCS buckets.

The exception is worth noting precisely because it is narrow and specific. Example datasets are fetched from public Google Cloud Storage buckets, which means the application does make outbound requests, just not ones carrying your data. The distinction matters for anyone who has to answer a question about where data goes, because the honest answer is that nothing leaves the tab, and a reader who assumes there is a server component will look for a privacy policy and an upload endpoint that do not exist.

The consequence for the user is that the application is a static single page app. There is no account, no saved dataset, no shared link, and no processing on a server you do not control. For an analyst with a sensitive extract, a customer list or a survey response file, that is the whole point. For a team that wants to share an analysis, it is the limitation.

The deployment shape confirms it. The Dockerfile has two stages, and the second one is an nginx that serves a static directory. There is no application server in the image, because there is no application server in the design.

The topic tags are data-science, econometrics, statistics and visualization, which tells you the intended user is someone doing exploratory analysis on tabular data, not a dashboard builder.

Type inference decides what a column can do

The first thing that happens after a CSV is loaded is type inference, and the README describes it plainly: the file is automatically parsed into one of Categorical, Number, or DateTime types.

Three types is a small vocabulary, and it is worth understanding what it buys, because the type of a column determines which charts it can appear in. The line plot is the clearest case, since the README says that only DateTime features will be available in the X-axis, and the scatter plot requires two numeric variables with the first chosen variable on the X-axis and the second on the Y-axis. A column that is inferred as Categorical cannot be plotted on a numeric axis, and the reverse is also true.

So the inference is not a cosmetic detail that happens in the background. It is the step that decides what you are able to do, and if the inference is wrong for a column, the option is simply absent rather than wrong. A numeric-looking column that contains a stray currency symbol is a realistic case where a column you wanted on an axis comes back as Categorical, and the fix is in the source data rather than in the tool.

The parsing itself is done with papaparse, which is in the dependency list, and that library is the conventional choice for CSV in the JavaScript ecosystem. Day.js handles the DateTime side, and the inference of a date column is therefore a matter of recognising a format.

For a data tool that never sends your file anywhere, the inference has to happen on the client, and that is also why the columns must be typed rather than sampled lazily on a server. The whole dataset is loaded into the browser, which is fine for the file sizes a spreadsheet user opens, and is the natural limit of the approach: the browser tab is the memory ceiling, and a CSV large enough to exhaust it is a CSV this tool cannot open.

Four charts and one regression engine

The feature set is five things, and each one is a specific analytical question.

The histogram answers what the distribution of a feature looks like, with the README noting the value of seeing whether a feature follows a particular distribution such as Gaussian, uniform or Poisson. There are also built-in feature transformation methods, square, natural log and log10, which is the practical fix for the skewed column that looks uninteresting until you take its logarithm.

The scatter plot handles relationships between two variables, with the first chosen variable on the X-axis and the second on the Y-axis. Hovering over an individual point shows the details of that instance, and you can optionally add filters to control for certain variable values or to look at specific sub-sections, with a reference list of supported operators. Filtering is what turns a scatter plot into an analysis tool, since it is how you look at a slice rather than the whole cloud.

The line plot is for trends and seasonality, and only DateTime features can go on the X-axis, with hovering over the line showing the values at that point in time.

The correlation matrix gives linear correlations for any pair of variables, and the colour convention is stated: purple indicates positive correlation and red indicates negative. A matrix of one colour would be a warning sign rather than a finding.

The modelling feature is a linear regression on the uploaded dataset, and the output is the part that matters. You get R-squared, standard errors, 95 percent confidence intervals, Z-scores and p-values displayed along with the coefficients for each explanatory variable. The README frames the point as quantifying your intuition, and the reason to care is that a coefficient without a p-value is an anecdote.

The implementation stack is d3 for the drawing, React 19 for the interface, react-spring for animation, and lodash-es for the data handling. That is a visualisation-first stack, which is consistent with a tool that renders rather than computes, and the modelling output is the one place where the tool reaches into statistics.

Building and running the development server

The development section is three lines, and the port is worth noting because it is not the port the production container uses:

bash
npm ci
npm run dev
open http://localhost:5173/

The dev server is Vite on 5173, while the nginx image serves on whatever port the deployment maps, which is a reminder that the local and production setups are two different things rather than one thing in two modes.

The package scripts show the rest of the toolchain. The build script is tsc followed by vite build, so type checking is part of the build rather than a separate command that might be skipped. The test script is vitest, and the lint script is eslint with --report-unused-disable-directives, --max-warnings 0 and --fix, so lint failures are errors and unused disable comments are themselves reported. The preview script serves the built output with vite preview.

The dependency versions are current rather than conservative, which matters for a project that ships a single static bundle. Vite 8, Vitest 4, Tailwind 4, ESLint 9, React 19 and TypeScript 5, with the React plugin for Vite built on SWC rather than Babel, and the SWC choice is the difference between a fast dev server and a slow one on a large codebase.

Two smaller entries are worth a mention. There is a vite-plugin-sitemap, which matters because the application is a static site that a search engine has to be able to crawl, and there is a rollup-plugin-visualizer, which is the sort of dependency a team adds when it wants to know what ended up in the bundle. Both suggest a project that is conscious of the artefact it ships.

The build output is a static directory served by nginx, and the Dockerfile's second stage compiles nginx 1.25.4 from source rather than using a packaged binary. That is a deliberate choice with a cost: compiling nginx in the image takes a build toolchain into the build stage and makes the image larger and slower to build, and in exchange you get exactly the modules you asked for, with the brotli module cloned from source as well.

Version drift, the licence and who should adopt this

Two small things in the metadata deserve a note before anyone evaluates the project.

The first is a version mismatch. The published releases are 0.9.4 from 2026-09-06, 0.9.3 from 2026-08-15 and 0.9.2 from 2026-04-06, and the last push to the repository was on 2026-10-20, so the project is moving. But the package.json in the repository still records the version as 0.9.2. Since the package is marked private, the version in that file is not what an installer sees, and the published artefacts are what matter, but it does mean the repository's own metadata lags the release line by two patch versions.

The second is the licence, which is MIT with the LICENSE file at the root of the repository. That is the permissive choice, and for a tool whose entire value proposition is that your data never leaves the browser, there is no copyleft complication to think about.

So who should adopt it. The obvious case is an analyst who has a CSV, does not want to upload it anywhere, and wants distributions, relationships and a regression in a couple of minutes rather than a Python environment. The application handles the type inference, the charts, the filters and the regression output, and the browser handles the data.

The cases where it is the wrong tool are equally clear. It is not a notebook, so there is no code execution, no custom transformation beyond the three built-in ones, and no way to model anything other than a linear regression. It is not a dashboard, so there is nothing to save and nothing to share. And it is not a data pipeline, because the file lives in a browser tab and disappears when the tab does.

The alternative approaches to the same task are a hosted notebook such as a hosted Jupyter environment, which gives you code and any model at the cost of uploading the data, and a local Python stack, which gives you code and keeps the data on your machine at the cost of a setup step. Visprex sits between the two, and its pitch is that for a first look at a file, neither the upload nor the setup is worth it.

Editorial conclusion

Adopt Visprex if you need to look at a CSV quickly and the data cannot leave your machine, since the README states that the processing happens entirely in the browser and that the only network calls are for example datasets, and if you want the regression output with R-squared, standard errors, confidence intervals and p-values rather than a picture. Do not adopt it as a replacement for a notebook, since the modelling is limited to linear regression and there is no code execution, and do not expect collaboration or persistence, because there is no backend to hold a dataset. Verify first which version you deploy, since the published releases reach 0.9.4 while the package.json still records 0.9.2, that the nginx image you run is built from source with the modules you expect, since the Dockerfile compiles its own nginx, and that your CSV parses into the three inferred types you expect, since the type inference is what decides which charts a column can appear in.

Frequently asked questions

Does Visprex send my data to a server?

The README states that the data is processed entirely in the browser without being sent to any backend servers, and that the only network calls the application makes are for downloading example datasets from public GCS buckets. The application is a static single page app with no application server in the image.

What column types does Visprex infer?

Once a CSV is loaded it is parsed into one of three types, Categorical, Number or DateTime. The type decides which charts a column can appear in, since the line plot only accepts DateTime features on the X-axis and the scatter plot takes two numeric variables.

What statistics does the Visprex modelling feature report?

Linear regressions on the uploaded dataset, with R-squared, standard errors, 95 percent confidence interval, Z-scores and p-values displayed along with the coefficients for each explanatory variable.

How do I run Visprex locally?

Run npm ci, then npm run dev, and open http://localhost:5173/. The dev server is Vite, the build is tsc followed by vite build, the tests run under vitest, and the lint script is eslint with --max-warnings 0 and --fix.

How is Visprex deployed?

The Dockerfile has two stages, a Vite build stage on node:20-alpine that runs npm ci and npm run build, and an nginx stage that compiles nginx 1.25.4 from source with the ssl, http/2, gzip static and brotli modules before serving the static output.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. visprex/visprex on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/visprex-visprex.svg)](https://hysenlabs.com/projects/visprex-visprex)

Was this useful?