<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Balaji K</title>
    <description>The latest articles on DEV Community by Balaji K (@buildwithbalaji).</description>
    <link>https://dev.to/buildwithbalaji</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2099333%2Faeeffc11-973f-4d05-9cdb-2f465b953bf1.png</url>
      <title>DEV Community: Balaji K</title>
      <link>https://dev.to/buildwithbalaji</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/buildwithbalaji"/>
    <language>en</language>
    <item>
      <title>Adding live sync to a Notion finance template without Zapier, Make, or a backend published: false</title>
      <dc:creator>Balaji K</dc:creator>
      <pubDate>Thu, 21 May 2026 00:20:26 +0000</pubDate>
      <link>https://dev.to/buildwithbalaji/adding-live-sync-to-a-notion-finance-template-without-zapier-make-or-a-backend-published-false-1pom</link>
      <guid>https://dev.to/buildwithbalaji/adding-live-sync-to-a-notion-finance-template-without-zapier-make-or-a-backend-published-false-1pom</guid>
      <description>&lt;h2&gt;
  
  
  How I turned WealthOS Lite into WealthOS Pro: a local-first Notion portfolio tracker with double-click sync for prices, SIP dates, and alerts.
&lt;/h2&gt;

&lt;p&gt;tags: notion, javascript, productivity, showdev&lt;/p&gt;

&lt;p&gt;A few weeks ago I wrote about shipping &lt;strong&gt;WealthOS Lite&lt;/strong&gt;: a free Notion finance template that intentionally did less.&lt;/p&gt;

&lt;p&gt;Two databases. A few formulas. No scripts. No API keys. No setup ceremony.&lt;/p&gt;

&lt;p&gt;That post was about restraint.&lt;/p&gt;

&lt;p&gt;This one is about the thing I deliberately left out of Lite:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;live sync.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most Notion finance templates run into the same wall eventually.&lt;/p&gt;

&lt;p&gt;Manual tracking is clean, but stale.&lt;/p&gt;

&lt;p&gt;Automation is useful, but the usual answer is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;connect Make.com&lt;/li&gt;
&lt;li&gt;connect Zapier&lt;/li&gt;
&lt;li&gt;pay monthly&lt;/li&gt;
&lt;li&gt;create a bunch of scenarios&lt;/li&gt;
&lt;li&gt;hope the integration does not break&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That did not feel right for a personal finance system.&lt;/p&gt;

&lt;p&gt;So for the Pro version, I tried a different architecture:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep Notion as the interface.&lt;br&gt;&lt;br&gt;
Keep the data in the user's workspace.&lt;br&gt;&lt;br&gt;
Run the sync locally on the user's own machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No hosted backend.&lt;br&gt;&lt;br&gt;
No Zapier.&lt;br&gt;&lt;br&gt;
No Make.&lt;br&gt;&lt;br&gt;
No subscription automation layer.&lt;/p&gt;

&lt;p&gt;Just Notion + a small local Node.js sync app.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l2w200layspj3oekct9.png" alt=" " width="800" height="796"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  The constraint
&lt;/h2&gt;

&lt;p&gt;I wanted WealthOS Pro to support a more complete portfolio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stocks&lt;/li&gt;
&lt;li&gt;mutual funds&lt;/li&gt;
&lt;li&gt;ETFs&lt;/li&gt;
&lt;li&gt;fixed deposits&lt;/li&gt;
&lt;li&gt;gold&lt;/li&gt;
&lt;li&gt;government bonds&lt;/li&gt;
&lt;li&gt;retirement accounts&lt;/li&gt;
&lt;li&gt;share plans&lt;/li&gt;
&lt;li&gt;real estate&lt;/li&gt;
&lt;li&gt;insurance&lt;/li&gt;
&lt;li&gt;loans&lt;/li&gt;
&lt;li&gt;financial goals&lt;/li&gt;
&lt;li&gt;tax savings&lt;/li&gt;
&lt;li&gt;alerts and reminders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I did not want to turn it into a SaaS product.&lt;/p&gt;

&lt;p&gt;That meant no central server storing user data.&lt;/p&gt;

&lt;p&gt;The sync app had to run locally.&lt;/p&gt;

&lt;p&gt;The user flow needed to be simple enough for non-developers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Download ZIP
2. Duplicate Notion template
3. Double-click launch.command or launch.bat
4. Complete browser setup
5. Click Run Sync
No terminal commands.

That last part mattered more than I expected.

As developers, we are very comfortable saying:

node wealthos-sync.mjs
For normal users, that is already too much.

So the real product was not the script.

The product was making the script disappear.

The architecture
At a high level, WealthOS Pro looks like this:

WealthOS Notion Workspace
        |
        |  Notion API
        |
Local Sync App
        |
        |-- Yahoo Finance
        |-- Finnhub
        |-- mfapi.in
        |-- gold-api.com
The local app is a small Node.js bundle.

It reads a local .env file:

NOTION_API_KEY=...
MASTER_PORTFOLIO_ID=...
FINNHUB_KEY=...
Then it updates the Notion databases directly.

The important part: the data does not pass through my server.

There is no WealthOS cloud.

The user's portfolio lives in Notion, and the sync process runs on their computer.

Why not just build a SaaS?
I considered it.

A SaaS would be easier to monetize, easier to update centrally, and easier to control.

But personal finance data has a different trust profile.

I did not want users to ask:

Where is my portfolio data stored?

The answer should be simple:

In your Notion workspace and on your own machine.

That is also why the sync bundle is inspectable. The user can open the files, read the code, and see what is happening.

For this product, local-first felt more honest.

The single source of truth
The core Notion database is called Master Portfolio.

Every holding goes there.

Stocks, mutual funds, deposits, gold, loans, insurance, retirement accounts, share plans — all of them are rows in the same database.

That gives the system one reliable model:

Master Portfolio
├── Asset Name
├── Category
├── Sub-type
├── Country
├── Ticker
├── Units / Qty
├── Buy Price
├── Invested Amount
├── Current Price
├── Current Value
├── Gain / Loss
├── Return %
├── CAGR %
├── Next SIP Date
├── Last Updated
└── Risk Level
Different pages in Notion are just linked views of the same source.

That design decision avoided a trap I see in many complex Notion templates:

too many duplicated summary databases.

If you update one holding, every linked view, chart, formula, and dashboard can reflect it.

The sync app only needs to update the source rows.

How price sync works
For each row, the sync script checks the category and ticker.

For example:

NSE:RELIANCE  -&amp;gt; RELIANCE.NS
LSE:SHEL      -&amp;gt; SHEL.L
PA:MC         -&amp;gt; MC.PA
US stocks can use Finnhub if the user provides a free key.

Non-US stocks can fall back to Yahoo Finance.

Indian mutual funds use mfapi.in.

Gold uses a public gold price API.

The update is intentionally boring:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;const props = {&lt;br&gt;
  "Current Price": { number: result.price },&lt;br&gt;
  "Last Updated": { date: { start: new Date().toISOString() } },&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;if (result.change != null) {&lt;br&gt;
  props["Day Change %"] = { number: result.change };&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;await updatePage(row.id, props);&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The sync app does not try to own the portfolio logic.

It just updates the few fields that should come from external data.

Notion formulas handle the rest.

The double-click launcher
This was surprisingly important.

The folder contains:

launch.command   // macOS
launch.bat       // Windows
install-node.html
setup-ui.html
wealthos-app.mjs
wealthos-sync.mjs
sync-portfolio-metrics.mjs
sync-sip-dates.mjs
sync-alerts.mjs
On macOS, users double-click:

launch.command
On Windows:

launch.bat
The launcher checks whether Node.js is installed.

If not, it opens a local install guide.

If yes, it starts the local web setup app.

That web app opens in the browser and walks the user through:

Notion integration token
Master Portfolio database ID
optional Finnhub API key
first sync
The goal was to make the developer workflow feel like a normal desktop app.

The part that broke during testing
One bug showed up late.

Prices worked.

Portfolio metrics worked.

SIP dates worked.

But alerts were skipped.

The output looked like this:

Refreshing Alerts &amp;amp; Reminders…
Missing NOTION_API_KEY, MASTER_PORTFOLIO_ID, or ALERTS_REMINDERS_ID

Sync Complete
Prices: 12 updated
Metrics: refreshed
SIP dates: refreshed
Alerts: skipped
The bug was not in the Notion API.

It was in the product assumption.

The setup UI told users:

Other databases are auto-discovered.

But sync-alerts.mjs still required ALERTS_REMINDERS_ID.

That mismatch is exactly the kind of thing that only appears when you test like a buyer.

The fix was to let the alerts sync search for the database by title:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;async function searchDatabaseByTitle(title) {&lt;br&gt;
  let cursor;&lt;br&gt;
  const wanted = title.toLowerCase();&lt;/p&gt;

&lt;p&gt;do {&lt;br&gt;
    const body = {&lt;br&gt;
      query: title,&lt;br&gt;
      page_size: 100,&lt;br&gt;
      filter: { property: "object", value: "database" },&lt;br&gt;
    };&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (cursor) body.start_cursor = cursor;

const data = await api("POST", "/search", body);
const exact = data.results.find(
  (r) =&amp;gt; titleOfDatabase(r).toLowerCase() === wanted
);

if (exact) return exact.id;

cursor = data.has_more ? data.next_cursor : null;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;} while (cursor);&lt;/p&gt;

&lt;p&gt;return null;&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Now the sync auto-discovers:

Alerts &amp;amp; Reminders
Insurance Policies
Then it saves those IDs locally for future runs.

That turned this:

Alerts: skipped
into this:

Alerts: refreshed
Small fix, big confidence boost.

What the sync updates
One sync run refreshes:

stock prices
mutual fund NAVs
gold prices
portfolio metrics
next SIP dates
alerts for SIPs
loan EMI reminders
maturity reminders
insurance renewal reminders
The final output looks like this:

Sync Complete

Prices:     12 updated, 1 skipped, 0 failed
Metrics:    refreshed
SIP dates:  refreshed
Alerts:     refreshed
Time:       93.7s
That “boring” summary is the whole point.

A user should not need to understand the internals.

They should just know the data refreshed.

Why I kept it zero-dependency
The sync bundle uses Node.js 18+ and native fetch.

No npm install.

No package manager.

No dependency tree.

That was intentional.

For a developer tool, dependencies are normal.

For a ZIP file that a customer downloads and double-clicks, every dependency is another support ticket waiting to happen.

So the constraint became:

Can this run with only Node.js installed?
That made the code a little more manual in places, but the install story became much cleaner.

What I learned
1. Automation has to feel calm
The user does not care that the Notion API call succeeded.

They care that their dashboard now looks updated.

2. Local-first is a product feature
For finance tools, privacy is not just a technical detail. It is part of the value proposition.

3. Notion is great as the interface layer
I did not need to build dashboards, tables, filters, and charts from scratch. Notion already does that well.

The local app only fills the gaps Notion cannot handle alone.

4. Testing the ZIP matters
Running the code from the repo is not enough.

The real test is:

download ZIP
extract ZIP
double-click launcher
follow setup
run sync
check Notion
That is where the product actually exists.

Where this fits
WealthOS Lite is still the best starting point if you want a simple free finance tracker inside Notion.

WealthOS Pro is for people who want the bigger system:

more asset classes
local sync
setup guide
videos
alerts
multi-country portfolio structure

I packaged the Pro version here:
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://balajibuilder.gumroad.com/l/wealthos" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;balajibuilder.gumroad.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The original open-source single-file finance app is here:&lt;/p&gt;

&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/balajiregt" rel="noopener noreferrer"&gt;
        balajiregt
      &lt;/a&gt; / &lt;a href="https://github.com/balajiregt/myFinance" rel="noopener noreferrer"&gt;
        myFinance
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Self-hosted, single-file Indian portfolio tracker with AI insights, Gmail expense tracking
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;₹ FinFolio — Complete Indian Portfolio Tracker&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Self-hosted, single-file, privacy-first portfolio tracker for Indian investors.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Track Stocks, Mutual Funds, FDs, RDs, SGB Gold, Physical Gold, Post Office schemes, Retirement (EPF/NPS), Real Estate, Insurance, Loans, Company Share Plans (ESPP/RSU/ESOP), and more — all from one dashboard with AI-powered insights. Auto-track expenses from bank emails via Gmail API. Proactive alerts via Telegram/WhatsApp.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Self-hosted means YOU own it.&lt;/strong&gt; Fork this repo. Run it locally, or deploy to your own Fly.io instance. Cloud backup goes to your own Supabase instance. All data stays in your browser by default — no shared servers, no tracking, no accounts.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why FinFolio?&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
&lt;thead&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;FinFolio Solution&lt;/th&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/thead&gt;
&lt;br&gt;
&lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;Groww/Zerodha only show what you buy through them&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;Tracks ALL assets — broker, manual, physical&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;INDMoney wants your bank login&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;Zero server-side data — 100% localStorage&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;No app tracks physical gold in your bank locker&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;Physical gold tracker with storage locations + photo&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/p&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/balajiregt/myFinance" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;p&gt;And the earlier post about shipping WealthOS Lite is here:&lt;/p&gt;

&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/buildwithbalaji/from-a-single-html-file-finance-app-to-a-notion-template-lessons-in-shipping-for-users-4588" class="crayons-story__hidden-navigation-link"&gt;From a single-HTML-file finance app to a Notion template — lessons in shipping for users🥹&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/buildwithbalaji" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2099333%2Faeeffc11-973f-4d05-9cdb-2f465b953bf1.png" alt="buildwithbalaji profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/buildwithbalaji" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Balaji K
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Balaji K
                
              
              &lt;div id="story-author-preview-content-3624256" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/buildwithbalaji" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2099333%2Faeeffc11-973f-4d05-9cdb-2f465b953bf1.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Balaji K&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/buildwithbalaji/from-a-single-html-file-finance-app-to-a-notion-template-lessons-in-shipping-for-users-4588" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 7&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/buildwithbalaji/from-a-single-html-file-finance-app-to-a-notion-template-lessons-in-shipping-for-users-4588" id="article-link-3624256"&gt;
          From a single-HTML-file finance app to a Notion template — lessons in shipping for users🥹
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/notion"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;notion&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/productivity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;productivity&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/buildwithbalaji/from-a-single-html-file-finance-app-to-a-notion-template-lessons-in-shipping-for-users-4588" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/buildwithbalaji/from-a-single-html-file-finance-app-to-a-notion-template-lessons-in-shipping-for-users-4588#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>personalfinance</category>
    </item>
    <item>
      <title>From a single-HTML-file finance app to a Notion template — lessons in shipping for users🥹</title>
      <dc:creator>Balaji K</dc:creator>
      <pubDate>Thu, 07 May 2026 13:23:52 +0000</pubDate>
      <link>https://dev.to/buildwithbalaji/from-a-single-html-file-finance-app-to-a-notion-template-lessons-in-shipping-for-users-4588</link>
      <guid>https://dev.to/buildwithbalaji/from-a-single-html-file-finance-app-to-a-notion-template-lessons-in-shipping-for-users-4588</guid>
      <description>&lt;p&gt;I've spent 12+ years writing automation systems for production platforms — test frameworks, API testing pipelines, the kind of code that runs invisibly so people upstream can ship faster.&lt;/p&gt;

&lt;p&gt;A few weeks ago I shipped Finfolio — a single-HTML-file personal finance tracker. No backend. No sign-up. Your data never leaves your browser. It tracks every asset class an Indian investor actually holds — mutual funds, SGBs, FDs, ESPP/RSU, PPF, NPS, real estate — pulls live NAVs from AMFI, has Gmail OAuth to auto-categorize bank alerts into expenses, and has an AI layer that analyzes your portfolio with your own API key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/balajiregt/myFinance" rel="noopener noreferrer"&gt;Repo here&lt;/a&gt;. MIT licensed. Free forever.&lt;/p&gt;

&lt;p&gt;This weekend I shipped a Notion template that does ~30% of what Finfolio does. And honestly, I think it might be more useful for more people.&lt;br&gt;
Here's why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson Finfolio taught me
&lt;/h2&gt;

&lt;p&gt;Finfolio works. I use it every day. People who like full-control technical tools genuinely like it.&lt;/p&gt;

&lt;p&gt;But here's what shipping it taught me: most people don't want a tool. They want a system.&lt;/p&gt;

&lt;p&gt;Finfolio asks: "What's your AMFI scheme code? Do you want to enable Gmail OAuth? Where do you want to host this — Netlify, Vercel, fly.io, or just open it locally?" Those are perfect questions for engineers. They're terrible questions for my mother-in-law who just wants to see if her FDs and SIPs are doing OK.&lt;/p&gt;

&lt;p&gt;The friction wasn't bugs or features. It was that the bar for entry was "be technically curious enough to enable Gmail OAuth." That's a small population.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I asked a different question:
&lt;/h2&gt;

&lt;p&gt;What if I rebuilt the core of Finfolio in a tool people already open every day, with a structure flexible enough to bend around their needs instead of mine?&lt;/p&gt;

&lt;p&gt;For me — and a lot of knowledge workers — that tool is Notion.&lt;br&gt;
So I started over with a hard constraint: two databases, three pages, and formulas that do all the work. No script to run. No API keys. No deployment step. Just duplicate and use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7gkz326wvwrg1fnhcm4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7gkz326wvwrg1fnhcm4.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
The constraint became the feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture: 2 databases, 3 pages
&lt;/h2&gt;

&lt;p&gt;Finfolio has 12+ asset class trackers, an expense module, a goals module, an alerts hub, and AI-powered analysis. It's a suite.&lt;br&gt;
The Notion version is the opposite. Aggressively minimal:&lt;br&gt;
WealthOS Lite/&lt;br&gt;
├── Dashboard (page)&lt;br&gt;
│   ├── Net worth callout&lt;br&gt;
│   └── Quick-start instructions&lt;br&gt;
├── Portfolio (database)&lt;br&gt;
│   ├── Asset Name (title)&lt;br&gt;
│   ├── Category (select: Stocks, Mutual Fund, FD, Gold, etc)&lt;br&gt;
│   ├── Invested Amount (number)&lt;br&gt;
│   ├── Current Value (number)&lt;br&gt;
│   ├── Gain/Loss (formula)&lt;br&gt;
│   └── Return % (formula)&lt;br&gt;
└── Budget (database)&lt;br&gt;
    ├── Description (title)&lt;br&gt;
    ├── Type (select: Income, Expense)&lt;br&gt;
    ├── Category (select)&lt;br&gt;
    ├── Amount (number)&lt;br&gt;
    └── Date (date)&lt;br&gt;
That's the whole thing. Two databases, three pages, six properties on Portfolio, five on Budget.&lt;/p&gt;

&lt;p&gt;This was the hardest design discipline in the entire project. I've already built the complete version — Finfolio knows about insurance renewals, EMI calculators, gold purity, post office schemes. Adding any of that to Lite would be one afternoon's work.&lt;/p&gt;

&lt;p&gt;But here's what I learned: the Lite version of anything is usually the better product. People who get value from 2 databases will reach for the 12-database version when they're ready. People who never start because the template is overwhelming never come back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 formulas that do all the work
&lt;/h2&gt;

&lt;p&gt;Notion formulas are limited in ways that frustrate engineers — no recursion, no loops, no array methods on relations. Coming from Finfolio's JavaScript codebase where I can do anything, this felt like a downgrade.&lt;br&gt;
It isn't. For personal finance, you don't need any of that. You need three formulas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxw9w9ezr83x5xav1urrx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxw9w9ezr83x5xav1urrx.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;br&gt;
Formula 1: Gain/Loss&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prop("Current Value") - prop("Invested Amount")&lt;/code&gt;&lt;br&gt;
That's it. The interesting part isn't the formula — it's that Notion recalculates it the moment you change either input. No sheet refreshes, no "press F9 to recalculate," no broken cell references when you reorder rows.&lt;br&gt;
In Finfolio, I had to write reactive state management to make this work. Notion gave it to me for free.&lt;/p&gt;

&lt;p&gt;Formula 2: Return %&lt;br&gt;
&lt;code&gt;if(&lt;br&gt;
  prop("Invested Amount") &amp;gt; 0,&lt;br&gt;
  round(&lt;br&gt;
    ((prop("Current Value") - prop("Invested Amount")) / prop("Invested Amount")) * 10000&lt;br&gt;
  ) / 100,&lt;br&gt;
  0&lt;br&gt;
)&lt;/code&gt;&lt;br&gt;
Three things going on here:&lt;/p&gt;

&lt;p&gt;Guard clause — if(prop("Invested Amount") &amp;gt; 0, ...) prevents division by zero when you create a new row before filling values.&lt;br&gt;
The math — standard return percentage formula.&lt;br&gt;
The round / 10000 * 100 dance — Notion's round() only rounds to integers. To get 2 decimal places, you multiply by 10000, round, divide by 100. Quirky but works.&lt;/p&gt;

&lt;p&gt;The third bit is the kind of gotcha you only learn by hitting it. Notion's docs don't really call it out.&lt;/p&gt;

&lt;p&gt;Formula 3: The conditional that handles asset-class quirks&lt;br&gt;
This is the formula I'm proudest of, and it's worth sharing because it solves a common gotcha that bites every personal-finance template author.&lt;br&gt;
When you add a Fixed Deposit to your portfolio, "Current Value" doesn't quite make sense — the value IS the invested amount until maturity. Same with cash holdings, recurring deposits, and bonds you're holding to maturity.&lt;/p&gt;

&lt;p&gt;In Finfolio I solved this with conditional rendering and per-asset-class form fields. In Notion, one formula:&lt;br&gt;
&lt;code&gt;if(&lt;br&gt;
  empty(prop("Current Value")),&lt;br&gt;
  prop("Invested Amount"),&lt;br&gt;
  prop("Current Value")&lt;br&gt;
)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Default to Invested Amount when Current Value is empty. Now FDs and cash holdings work correctly without users needing to type the same number twice. Small thing, big UX win.&lt;/p&gt;

&lt;p&gt;The pattern from Finfolio that survived: smart defaults beat configuration every time.&lt;/p&gt;

&lt;p&gt;The screenshot you don't see in product photos&lt;br&gt;
Most Notion template marketing shows the dashboard view — clean, populated, looks great.&lt;/p&gt;

&lt;p&gt;What it doesn't show is the Notion creator's secret weapon: linked database views.&lt;/p&gt;

&lt;p&gt;The Portfolio database lives in one place. But on the Dashboard page, I render it again as:&lt;/p&gt;

&lt;p&gt;A grouped view (by Category) with sum aggregations&lt;br&gt;
A donut chart (Notion's chart blocks read directly from databases)&lt;br&gt;
A filtered "top gainers" view&lt;/p&gt;

&lt;p&gt;Same database, three different visualizations. Updating one row updates every view simultaneously, including the chart. No refresh, no sync, no broken state.&lt;/p&gt;

&lt;p&gt;This is the same separation of concerns I'd apply to any system — model, view, controller. Notion just makes the "view" layer ridiculously easy.&lt;/p&gt;

&lt;p&gt;In Finfolio I wrote ~400 lines of Chart.js and DOM manipulation to do what Notion gives me out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I left out for v1 (and why)
&lt;/h2&gt;

&lt;p&gt;This is where Finfolio's complexity contrasted hardest with the discipline a marketplace template requires.&lt;br&gt;
I had a list of ~15 features I wanted to ship. I cut it to 6.&lt;br&gt;
What didn't make it to Lite:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ Live price sync — needs a companion script. Out of scope for a free Notion-only template.&lt;br&gt;
❌ Gmail OAuth for expenses — Finfolio's killer feature. Notion can't do OAuth flows. Will need a different solution.&lt;br&gt;
❌ Multi-currency support — sounds simple, balloons fast. FX rates? Historical conversion? Display vs storage?&lt;br&gt;
❌ Goals, SIP planner, tax modules — each is its own database. Pulled them out to keep Lite lite.&lt;br&gt;
❌ AI analysis — Finfolio has it (bring your own key). Notion has Notion AI but the integration story for templates isn't clean yet.&lt;br&gt;
❌ Mobile-optimized layouts — Notion handles this OK by default. Custom mobile design = duplicate page structures = maintenance burden.&lt;br&gt;
❌ Auto-categorization — would need formulas with brittle string matching. Manual is fine for v1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The rule I kept telling myself: "If I can't ship it cleanly in v1, it goes in the v2 backlog. Period."&lt;/p&gt;

&lt;p&gt;Most template creators on the marketplace fail because they ship 40-feature behemoths nobody can figure out. The Finfolio version of WealthOS exists — it's the GitHub repo. The Notion version had to be different.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "shipping" actually felt like
&lt;/h2&gt;

&lt;p&gt;I want to be honest about this because dev.to readers are also makers, and we don't talk about it enough.&lt;br&gt;
For Finfolio, I knew the workflow: write code, test, commit, push, deploy. Even when it took a weekend, the process was familiar.&lt;br&gt;
For a Notion Marketplace template, the technical work was maybe 30% of the total time. The other 70% was things I genuinely didn't anticipate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Naming things — went through 11 names before settling on WealthOS. Each rejection ("taken on Gumroad," "trademarked elsewhere," "weird connotation in Hindi") was its own time sink.&lt;/li&gt;
&lt;li&gt;Designing covers and gallery images — Notion Marketplace requires specific dimensions (2048×1280 desktop, 750×1500 mobile) with strict safe-zone rules. Got rejected once for cropped content I thought looked fine.&lt;/li&gt;
&lt;li&gt;Writing the description — version after version. The first draft was technical. The second was sales-y. The fifth one finally felt right.&lt;/li&gt;
&lt;li&gt;Removing every promotional reference before submission. Notion's editorial guidelines flag templates that "primarily advertise paid products." Even one upgrade callout in the template body can trigger a rejection.&lt;/li&gt;
&lt;li&gt;Setting up creator profile, banner, bio, social links — all the meta-stuff that has nothing to do with the product but determines whether anyone clicks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compared to Finfolio (where I just needed git push and a GitHub README), shipping a marketplace template is closer to launching a small product. The ratio of "actual building" to "preparing the building to be seen" was inverse to what I expected as an engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;WealthOS Lite is on Notion Marketplace as a free template — the foundation. Two databases, formulas, charts, your data stays in your workspace.&lt;/p&gt;

&lt;p&gt;There's a Pro version I'm working on that connects back to the Finfolio architecture — a small Node.js companion app for live price syncing across stocks, mutual funds, and gold. Same "your data never leaves your machine" philosophy as Finfolio, but bridged into Notion. More on that when it's ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The bigger lesson from this whole exercise:&lt;/em&gt;&lt;/strong&gt; the same problem can have radically different shapes depending on who you're solving it for. Finfolio is right for people who want full control. WealthOS Lite is right for people who want a clean system in a tool they already use. Neither is "better" — they're just for different people.&lt;/p&gt;

&lt;p&gt;If you're building personal finance things, Notion templates, or anything else for non-technical users coming from technical roots, I'd love to compare notes.&lt;/p&gt;

&lt;p&gt;Try WealthOS Lite (free): [&lt;a href="https://stirring-heart-701.notion.site/WealthOS-Lite-34b3afb1ab1a81bf98f6e72a26551f50" rel="noopener noreferrer"&gt;https://stirring-heart-701.notion.site/WealthOS-Lite-34b3afb1ab1a81bf98f6e72a26551f50&lt;/a&gt;]&lt;br&gt;
Finfolio (the original): github.com/balajiregt/myFinance&lt;br&gt;
Building in public: &lt;a class="mentioned-user" href="https://dev.to/buildwithbalaji"&gt;@buildwithbalaji&lt;/a&gt; on X&lt;/p&gt;

&lt;p&gt;I'm planning to write more about the engineering decisions behind shipping consumer-facing products as someone whose day job is automation infrastructure. If that's interesting, let me know what you'd want to read next.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>notion</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>✨ One HTML file. No backend. No sign-up. Your data never leaves your browser.</title>
      <dc:creator>Balaji K</dc:creator>
      <pubDate>Fri, 01 May 2026 07:32:22 +0000</pubDate>
      <link>https://dev.to/buildwithbalaji/one-html-file-no-backend-no-sign-up-your-data-never-leaves-your-browser-3oj9</link>
      <guid>https://dev.to/buildwithbalaji/one-html-file-no-backend-no-sign-up-your-data-never-leaves-your-browser-3oj9</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;But here's what makes it different from the 50 other trackers out there:&lt;/p&gt;

&lt;p&gt;→ Real-time broker sync for Mutual Funds Live NAV pulled from AMFI. Actual real-time sync with your broker holdings.&lt;/p&gt;

&lt;p&gt;→ Gmail-synced expense tracking Connect Gmail (readonly OAuth) → finds your HDFC/ICICI/SBI bank alert emails → AI auto-parses merchant, amount, date, category → donut chart of where your money actually goes, plus budget vs actual comparison.&lt;/p&gt;

&lt;p&gt;→ AI that analyses YOUR portfolio Bring your own API key. The AI looks at your actual holdings, your actual allocation, your actual gaps not some template "diversify more" response.&lt;/p&gt;

&lt;p&gt;→ Export your financial profile to local Ollama Feed your complete snapshot to a local LLM. Your data stays on your machine. Run whatever analysis you want, privately.&lt;/p&gt;

&lt;p&gt;→ Supabase cloud backup (optional, your own instance) Want sync across devices? Plug in your own Supabase. Don't want it? Everything works locally.&lt;/p&gt;

&lt;p&gt;→ Reminders &amp;amp; Alerts hub Auto-generated from your insurance renewals, loan EMIs, FD maturities, SIP dates. Priority-based: overdue → urgent upcoming. No manual setup needed.&lt;/p&gt;

&lt;p&gt;And it tracks everything Indian investors actually hold:&lt;br&gt;
→ Mutual Funds, Stocks, ESPP/RSU/ESOP → Fixed &amp;amp; Recurring Deposits (multi-bank, maturity alerts) → Sovereign Gold Bonds (live spot price → INR) → Physical Gold (jewellery, coins purity, storage, photos) → Post Office schemes (PPF, SSY, NSC, MIS) → Insurance (term, health, endowment, vehicle — renewal tracking) → Loans (EMI tracking + prepayment calculator) → Real Estate (location, area, rental yield) → NPS, EPF, retirement planning&lt;/p&gt;

&lt;p&gt;Dashboard gives you net worth across every asset class, allocation charts with diversification scoring, and dark/light theme with a clean Bootstrap UI.&lt;/p&gt;

&lt;p&gt;Fork it. Deploy to Netlify/Vercel/fly.io. Or just double-click and open in your browser.&lt;/p&gt;

&lt;p&gt;Next up: an agent layer that messages you on Telegram when your FD matures or your MF NAV drops 5%. More on that soon.&lt;/p&gt;

&lt;p&gt;MIT Licensed. Free forever.&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://www.linkedin.com/posts/kbalaji-kks_finfolio-activity-7447868974194720768-E3CY?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAA9LqPABRzpr8yo2ODcz6XIs182lmEqHPxY" rel="noopener noreferrer"&gt;https://www.linkedin.com/posts/kbalaji-kks_finfolio-activity-7447868974194720768-E3CY?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAA9LqPABRzpr8yo2ODcz6XIs182lmEqHPxY&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;[&lt;a href="https://github.com/balajiregt/myFinance" rel="noopener noreferrer"&gt;https://github.com/balajiregt/myFinance&lt;/a&gt;]&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>sideprojects</category>
      <category>personalfinance</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
