- Jh123x: Blog, Code, Fun and everything in between./
- My Blog Posts and Stories/
- Building a Data Converter That Eliminates 4 Clicks per Task/
Building a Data Converter That Eliminates 4 Clicks per Task
Table of Contents
Introduction #
It’s been a while since my last blog post — and truthfully, that’s because I’ve been buried in work and data. Every day I found myself with the same task: writing scripts to convert data from one format to another and passing it along.
It sounds trivial, but after the hundredth time it felt like a productivity leak I couldn’t ignore.
I tried many online tools, but most of them jump through unnecessary clicks and selections. What should have taken seconds took much longer - and that was the spark for this project.
So I built my own data conversion tool: something that fits my workflow, gets out of the way, and lets me convert between data formats with far fewer interactions. What started as a simple time-saver quickly grew into a deeper learning journey into React, performance optimization, and real-world UI trade-offs — and that’s what this post is about.
In the sections that follow, I’ll walk through how the tool evolved, what problems I ran into, the lessons I learned (both small and big), and how this helped me build a faster, better, more usable tool for everyone — not just myself.
Summary #
Before (Typical Converter websites / My MVP) #
Typical Experience: 1 Paste + 5 clicks
- 1 set of copy-paste from external to current page
- 2 clicks to select the input format
- 2 clicks to select the output format
- 1 click to copy the results
After #
1 Paste + 1 click
- 1 set of copy-paste from external to current page
2 clicks to select the input format2 clicks to select the output format1 click to copy the results- 1 Click to get the desired result
Getting started #
When I first sat down to start this project, I didn’t just want to solve the immediate problem — I also wanted to stretch my frontend skills a bit. I’ve used Material UI in a lot of my past projects, and while it’s great, I figured this was a good opportunity to explore something different. That led me to Ant Design (Antd) — a UI library I hadn’t used much before.
Choosing Antd wasn’t a deep technical decision so much as a personal one: I wanted to get out of my usual habits and see how a different toolkit would feel. That choice ended up influencing a lot of the UI and component patterns I used later on.
Requirements #
Before writing a single line of code, I took a step back and asked myself a few simple questions:
- What do I actually need this tool to do?
- What would make it useful?
- How can I make sure it fits into my workflow?
Since this was a tool I expected to use frequently at work, it had to feel effortless in my workflow.
Current workflow #
My current workflow for dealing with data looks something like this
Similar to most workflows, I deal with a few different data formats.
| Format | Use Case |
|---|---|
CSV | To send to someone else to process or view. |
TSV | Copy & pasting directly to online sheets like Google Sheets / Lark Sheets. |
JSON | Used for calling other tool endpoints. |
For each format, the goal was simple: take something I already had, convert it to the desired format quickly.
Required Features #
To use the tool more effectively, I identified the following required features:
- Output formats should be easily selectable in various forms.
- It should work without any lag for inputs up to 10,000 rows.
There are also several optimizations I wanted to work towards.
- Minimize the number of clicks required to get from input format to output format.
- Minimize response times of the application and not block the main thread as much as possible.
Armed with my requirements, I started to venture into creating the product.
Development #
There were many hurdles I faced during development. However, I have learned a lot in the process as well. It has taught me a great deal about frontend and how React renders components on the page.
Initially I was using Create React App. However, after searching online, this approach is now generally discouraged in favor of faster modern tooling. This prompted me to try out Vite for bootstrapping this React app.
Overall Architecture #
After coming up with the requirements for the app, I designed a high level architecture on how my code is going to be. The flow I had in mind was something like this:
IR here refers to the intermediate representation of my choosing.
When coming up with the idea, I left this representation open and used JSON as a default as many TypeScript libraries already supported this.
Iteration 1: Minimum Viable Product #
After coming up with the high level architecture, I started work on implementing the details.
In my mind I wanted something that looks similar to the above wireframe. It had a dropdown to choose which language I wanted to input and an output area to select the results that I want.
The workflow for using the site looked something like this:
- Copy the data that I wanted to convert.
- Paste the data into the website’s input field
- Select the input data format
- Select the output data format
- Copy the results.
This workflow required:
- 1 set of copy-paste from external to current page
- 2 clicks to select the input format
- 2 clicks to select the output format
- 1 click to copy the results
Results #
As this is the initial MVP, I just wanted to see it working. The website definitely works as it is, but it is nowhere near what I want it to be. I had to iterate further to improve it so that my workflow is faster when I use it.
Learning #
During this section, I learned a lot about frontend programming.
- Setting up React with Vite.
- Learning Antd’s API and how its patterns differ from other UI libraries.
Personally, I am unlikely to switch from my other UI libraries to Antd, but my experience using Vite is quite a positive one, and it will likely be my default way to start a new React project in the future.
On top of the MVP, I also added web workers for parsing the input format as well as converting the data from an Intermediate representation to the respective output format.
Iteration 2: Reducing usage friction #
After using it for a bit, I thought of some ideas that I can implement in order to improve the usage experience of this page.
During the usage of this website, I found some points to optimize for.
- To reduce clicks on input selection, we can find some way to auto-detect the input format.
- To reduce clicks on output selection, we can have buttons for all possible outputs, given that there are only 3 possible outputs.
I ended up with a UI similar to the one above.
Auto-detection #
As the auto-detection is a toggle, the user can select the correct format if auto-detect is wrong.
JSON arrays.This is not a perfect heuristic, however, it is good enough for what I wanted to do.
This heuristic works well for my datasets but has limitations:
CSVfiles containing tab characters may be misclassified- Single-column
TSVis indistinguishable fromCSV
This will reduce the 2 clicks that were previously required.
Output Selection #
As there were only 3 formats to output, we can create buttons for all 3 output formats. Most of the time, when using the tool, I only do the following tasks:
- Pasting the file into an empty file for use later.
- Copy the text into a clipboard to paste it to somewhere else.
By creating buttons for these actions, we can reduce the clicks required for selecting the output to 2 clicks
Learnings #
By using my own website, I get to learn a lot about the pros and cons of this site. This is a good way to find out how to make the website better and what to prioritize.
In this case, it helped me make the site better (IMO) by reducing the number of clicks required to complete a given task.
Results #
As a result, the workflow improved to:
- 1 set of copy-paste from external to current page
2 clicks to select the input format2 clicks to select the output format1 click to copy the results- 1 Click to get the desired result
This reduced a lot of the clicks that were there in the beginning. In its current state, it is mostly usable for smaller data sets.
However, there are some cases where it is not working very effectively. When the data size is ~10k rows, the page lags when trying to convert the data.
This is probably due to the lack of optimization while I was implementing all of these, which brings us to the next iteration.
Iteration 3: Optimizing performance #
Due to the performance issues which I have encountered after my previous iteration, my next iteration focuses on optimizing the performance of the website.
When I paste in a large dataset containing 10k rows, the website will lag and eventually crash.
Initially, I thought that the website was lagging due to the slow compute due to the JSON IR.
However, I only had a hunch and even after implementing a different IR, I saw no visible improvements.
I also went down a few other dead ends while trying to improve performance of the page.
After having a conversation with my colleagues about this side project, they recommend that I benchmark the performance of the page. I can make use of the flame graph to determine which is the bottleneck of the page.
Benchmarking the webpage #
I am doing development on my MacBook Air for this project.
Initially, I was using Safari and was finding extensions to help me benchmark the React pages.
However, after asking my colleagues, Chrome is commonly used to profile React applications, partly due to strong DevTools support and the large Chromium market share. So I reluctantly downloaded Google Chrome to profile the app.
I used the React Developer Chrome Extensions to run my benchmark.
I started the profiling on the Profiler and pasted in a 5000 line sample CSV file
(Taken from GitHub).
After scrolling through the events, I found this event that took 21.2 seconds to load. The flame graph shows the rendering of the output box.

This showed the true bottleneck of the page. It is not even the main part of the web page that I usually use…
Optimizing the Rendering #
Now that I know that the bottleneck is from the Code Editor component using the SyntaxHighlighter, this is the part that I will have to optimize.
There are 2 different cases to take care of.
- I do not want to use the Code Editor component to look at the result data
- I want to take a brief look at the data from the results
For each of the cases, we can perform some optimizations
For cases where I do not need the code component, I can simply hide it behind a Antd Collapse, so it will not be mounted until it is required.
The more tricky case is when I need to render the text. There are 2 different parts of this.
- During the rendering process, the UI became unresponsive, and the user is unable to change other stuff.
- We need to provide hints to the users that the code editor is loading while still allowing the user to access the other features on the page.
To address these issues, I added:
- Faster Syntax Highlighter using the
@speed-highlight/corelibrary (With caveats explained later). - Add a suspense with a loading fallback for the Syntax Highlighting.
Result #
After doing the above optimizations, the results were much better without rendering the code.

The time for loading the page went from 21.2 seconds to just merely 13.1 milliseconds. Hiding the output component shaved the time by more than 99%

The loading of the code highlighting component also improved to 4.3 seconds if the user indeed wants to view the output of the results.
On top of the performance improvements, the user experience is also a lot better as the main thread does not block user interactions.
With the combination of the 2 fixes, the webpage responded faster and with better user experience in between.
Learnings #
During this iteration, I learned many technical things about React.
- When an object is not needed in the React Virtual DOM (Document Object Model), it is not loaded.
- To keep a suspense in fallback state, we need to throw a promise that will make the component load after it is resolved.
Point 2 is very important for integrating with the new Syntax Highlighting library as it mutates the DOM elements directly without the Virtual DOM. Before I did it this way, the component will load immediately without any highlighting before shifting the layout as the text is replaced by the highlighted DOM elements.
This took me a long time to debug and get right.
Most importantly, it taught me about benchmarking React applications and the general workflow for finding performance bottlenecks.
JSON was still the default IR that I was using.
This took a long time for my debugging due to ordering of keys and nested JSON objects.Future features #
For my main workflow, this website is considered feature complete, as I continue using the tool, I’ll fix any bugs I encounter along the way. However, there are many other features I would like to explore about frontend.
This website will likely act as a base for me to test such features.
EG:
- Programming video tutorials that auto updates with components
- Other tool types that are complementary to current features
Stay tuned to my future blog posts to find out more.
Conclusion #
In this blog post, I shared about my journey of creating a tool to help me out during work. During this process, I learned a lot about TypeScript, React and frontend development in general.
There are also many details of the implementation that I do not have the space to go through in this blog:
- Debouncing the processing
- Notifications to users
- Details of the worker threads
- Making raw JavaScript and React play well together.
- Etc.
For those that want to learn more about frontend, I definitely recommend building frontend projects that solve real problems.
Solving the problem acts as a form of motivation, especially if it can save you a lot of time.
Hope this blog post has helped you out and thank you for joining me in my journey to build this website.
Please give this tool a try and let me know if you have any suggestions.
If there are any other features that you want to add to this page, feel free to add a merge request through GitHub.