<?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: Rodion Kazennov</title>
    <description>The latest articles on DEV Community by Rodion Kazennov (@nefayran).</description>
    <link>https://dev.to/nefayran</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3643130%2Fc65fbbf1-6ab9-44e2-a28e-96e69769e9ce.jpg</url>
      <title>DEV Community: Rodion Kazennov</title>
      <link>https://dev.to/nefayran</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nefayran"/>
    <language>en</language>
    <item>
      <title>Where a code graph pays off: big repositories, small models</title>
      <dc:creator>Rodion Kazennov</dc:creator>
      <pubDate>Thu, 01 Oct 2026 14:53:18 +0000</pubDate>
      <link>https://dev.to/nefayran/where-a-code-graph-pays-off-big-repositories-small-models-30h3</link>
      <guid>https://dev.to/nefayran/where-a-code-graph-pays-off-big-repositories-small-models-30h3</guid>
      <description>&lt;p&gt;&lt;a href="https://allkeep.org/en/lab/code-graph-vs-grep" rel="noopener noreferrer"&gt;My last benchmark&lt;/a&gt; said a code graph did not make my coding agent cheaper. It was too small to say more than that: 24 questions, nothing bigger than 25k graph nodes, no Opus. So I ran it again at a size where the answer could change, and wrote the plan down first. The hypotheses, repositories, budget and analysis are in &lt;a href="https://github.com/nefayran/code-graph-vs-grep/blob/main/bench/PLAN-v2.md" rel="noopener noreferrer"&gt;PLAN-v2.md&lt;/a&gt;, committed before any run.&lt;/p&gt;

&lt;p&gt;The short version: a graph pays when the model would otherwise keep searching. Haiku on kubernetes and vscode is that case. Sonnet saves a little there and opus nothing, and on a small repository the graph costs sonnet more than plain grep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;The agent is GitHub Copilot CLI 1.0.90, run headless, one question per session, with two arms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grep: Copilot's view, grep and glob tools and nothing else;&lt;/li&gt;
&lt;li&gt;graph: the same three tools, plus cbm-lean (my thin wrapper around codebase-memory-mcp 0.9.0) and a routing rule at the top of the prompt: the graph for callers, callees and call chains, grep for exact strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Four public repositories, each pinned to a commit:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;repository&lt;/th&gt;
&lt;th&gt;language&lt;/th&gt;
&lt;th&gt;graph nodes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;full-stack-fastapi-template&lt;/td&gt;
&lt;td&gt;Python, TypeScript&lt;/td&gt;
&lt;td&gt;1,601&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dub&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;25,023&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kubernetes&lt;/td&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;146,421&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vscode&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;218,883&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each repository has 16 questions. Eight are structural: who calls a function, what it calls, the call path from a CLI command or an HTTP handler down to a named function, and which functions break if a signature changes. Eight are exact: a config value, where an environment variable is read, the line an error is raised on, the line a type is defined on. I checked every answer key by hand at the pinned commit, and an entry counts only as a whole word, so &lt;code&gt;init_db&lt;/code&gt; does not pass for &lt;code&gt;init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Haiku 4.5 answered all 64 questions three times in each arm, and sonnet 5 twice. Opus 5.5 costs 15 premium requests a prompt in Copilot, so it answered twelve structural questions from kubernetes and vscode once. That is 664 runs and about 744 premium requests.&lt;/p&gt;

&lt;p&gt;Cost is tokenEquiv, as in the first note: uncached input, plus 1.25 × cache writes, plus 0.1 × cache reads, summed over the model calls of a run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;The graph's cost as a share of grep's: for each question the graph arm's median over repetitions divided by the grep arm's, then the median over questions, with a 95% bootstrap interval. Below 1 the graph is cheaper.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;repository size&lt;/th&gt;
&lt;th&gt;structural&lt;/th&gt;
&lt;th&gt;exact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;haiku 4.5&lt;/td&gt;
&lt;td&gt;small (1.6k nodes)&lt;/td&gt;
&lt;td&gt;0.88 (0.65–1.29)&lt;/td&gt;
&lt;td&gt;0.92 (0.72–1.15)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;haiku 4.5&lt;/td&gt;
&lt;td&gt;medium (25k)&lt;/td&gt;
&lt;td&gt;0.61 (0.11–1.50)&lt;/td&gt;
&lt;td&gt;0.96 (0.75–1.37)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;haiku 4.5&lt;/td&gt;
&lt;td&gt;large (146k, 219k)&lt;/td&gt;
&lt;td&gt;0.29 (0.11–0.70)&lt;/td&gt;
&lt;td&gt;1.01 (0.94–1.28)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet 5&lt;/td&gt;
&lt;td&gt;small&lt;/td&gt;
&lt;td&gt;1.51 (1.45–1.70)&lt;/td&gt;
&lt;td&gt;1.42 (1.20–1.71)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet 5&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;td&gt;0.94 (0.57–2.00)&lt;/td&gt;
&lt;td&gt;1.14 (1.05–1.30)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet 5&lt;/td&gt;
&lt;td&gt;large&lt;/td&gt;
&lt;td&gt;0.74 (0.62–0.92)&lt;/td&gt;
&lt;td&gt;1.13 (1.01–1.29)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opus 5.5&lt;/td&gt;
&lt;td&gt;large&lt;/td&gt;
&lt;td&gt;0.97 (0.72–1.10)&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Correct answers on the structural questions, graph against grep:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;small&lt;/th&gt;
&lt;th&gt;medium&lt;/th&gt;
&lt;th&gt;large&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;haiku 4.5&lt;/td&gt;
&lt;td&gt;13/24 against 19/24&lt;/td&gt;
&lt;td&gt;20/24 against 22/24&lt;/td&gt;
&lt;td&gt;38/47 against 33/48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sonnet 5&lt;/td&gt;
&lt;td&gt;13/16 against 15/16&lt;/td&gt;
&lt;td&gt;16/16 against 16/16&lt;/td&gt;
&lt;td&gt;32/32 against 28/32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opus 5.5&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;12/12 against 12/12&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cost and accuracy together, as tokenEquiv per correct answer on the structural questions:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frn7ovoyvmtt3tplvctg9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frn7ovoyvmtt3tplvctg9.png" alt="Tokens per correct answer on structural questions, with the code graph and with grep only, on the FastAPI template and on kubernetes and vscode" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What holds up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On kubernetes and vscode, haiku with the graph spent 0.29 of what it spent with grep on structural questions and was cheaper on 15 of 16 of them. It was also right more often, so per correct answer the gap is 47k against 209k tokenEquiv. This is the only result that survives the Holm correction across the 13 tests (p = 0.006).&lt;/li&gt;
&lt;li&gt;Sonnet on the same questions: 0.74, cheaper on 12 of 16, and 32 of 32 correct against 28 of 32. Opus: 0.97, cheaper on 7 of 12, and every answer right in both arms.&lt;/li&gt;
&lt;li&gt;On the small repository the graph never paid. Haiku spent about the same and got structural questions wrong more often. Sonnet spent 42 to 51% more.&lt;/li&gt;
&lt;li&gt;On exact questions the graph made no measurable difference for haiku, and sonnet paid 13 to 42% more for it.&lt;/li&gt;
&lt;li&gt;The graph's tool schemas and the routing rule add a fixed 1.56k prompt tokens to every model call for haiku and 2.07k for sonnet and opus, at every repository size.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the small model gains most
&lt;/h2&gt;

&lt;p&gt;Count the model calls. With grep, haiku needed a median of 22 calls per structural answer on kubernetes and 20 on vscode, and one impact question took 114. With the graph it needed 5 and 4. Every call sends the conversation again, so a long search costs far more than its call count suggests: haiku's most expensive grep answer came to 471k tokenEquiv.&lt;/p&gt;

&lt;p&gt;Sonnet's searches with grep were shorter, 8 calls against 4 with the graph. Opus took 4 or 5 calls either way, so the graph had nothing to cut, and its fixed overhead ate what little it saved.&lt;/p&gt;

&lt;p&gt;My guess, which I did not test directly: a graph replaces search turns, so it pays when the model would otherwise take many of them, which means a weaker model on a larger repository. A strong model already searches efficiently with grep, and on a small repository nobody needs many turns.&lt;/p&gt;

&lt;p&gt;Copilot bills premium requests per prompt, whatever the number of calls, so inside Copilot the graph saves no money. It saves time: haiku's structural answers on the large repositories took a median of 71 s with the graph and 157 s with grep. On the small repository the graph arm was slower.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each arm went wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A field called &lt;code&gt;lines&lt;/code&gt;. Asked on which line &lt;code&gt;HorizontalController&lt;/code&gt; is defined, the graph arm answered horizontal.go:43 in all five runs, haiku and sonnet alike. The struct starts on line 93 and is 43 lines long. The snippet that codebase-memory-mcp returns carries &lt;code&gt;"start_line": 93&lt;/code&gt;, &lt;code&gt;"end_line": 135&lt;/code&gt; and &lt;code&gt;"lines": 43&lt;/code&gt;, and the models read the last one as the line number. It happened again with &lt;code&gt;LemonSqueezyClient&lt;/code&gt; (client.ts:129 for a class on line 59 that is 129 lines long), and haiku did the same with &lt;code&gt;CursorsController&lt;/code&gt;. My first note blamed "line numbers from the graph" for that client.ts:129; this is where it came from. Sonnet with grep got all three right. cbm-lean now passes the field on as &lt;code&gt;line_count&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Holes in the graph, trusted. codebase-memory-mcp 0.9.0 records no call edge for a bare call to a function imported with &lt;code&gt;from x import y&lt;/code&gt;. Asked what &lt;code&gt;recover_password&lt;/code&gt; calls, the graph arm answered "only get_user_by_email" in all five runs, haiku and sonnet alike, straight from the graph and without a single grep. The function also calls &lt;code&gt;generate_password_reset_token&lt;/code&gt;, &lt;code&gt;generate_reset_password_email&lt;/code&gt; and &lt;code&gt;send_email&lt;/code&gt;, and the grep arm found all four every time.&lt;/li&gt;
&lt;li&gt;A missing dash. Copilot's grep tool shows line numbers only when it gets &lt;code&gt;"-n": true&lt;/code&gt;, and its view tool returns a file without them. Sonnet passed &lt;code&gt;-n&lt;/code&gt; in 508 of its 571 grep calls and opus in all 53. Haiku passed &lt;code&gt;n&lt;/code&gt;, without the dash, 920 times and &lt;code&gt;-n&lt;/code&gt; 22 times out of 1,949. The tool ignores the unknown key, so haiku got no numbers, counted lines in the viewed file and missed by a few: for an error raised on line 112 it answered 105, 107, 108 and 113. All but one of haiku's 79 wrong answers to exact questions are a wrong line number.&lt;/li&gt;
&lt;li&gt;Long searches that still fail. On the call chain from &lt;code&gt;kubeadm token create&lt;/code&gt; down to &lt;code&gt;encodeTokenSecretData&lt;/code&gt;, haiku with grep took 18 to 22 calls and got the path wrong all three times. It was wrong with the graph too. Sonnet was right in both arms.&lt;/li&gt;
&lt;li&gt;Haiku tried to call bash three times in the grep arm. Copilot refused, since the tool does not exist there, so no run used a tool outside its arm.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What changed since the first note
&lt;/h2&gt;

&lt;p&gt;The first note said not to expect token savings from a graph with Claude models on repositories up to 25k nodes. That still holds at those sizes. It missed what happens above them: on kubernetes and vscode the smaller models gain, and the smaller the model, the more.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell a team wiring a graph into an agent
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Measure on your largest repository with your cheapest model first. That is where a graph earns its place.&lt;/li&gt;
&lt;li&gt;With a strong model on a small or medium repository, leave it out. You pay for its schemas on every call and get nothing back.&lt;/li&gt;
&lt;li&gt;Keep routing exact questions to grep. The graph never won them.&lt;/li&gt;
&lt;li&gt;Do not let the model take line numbers or "this is the complete list" from graph output on trust. Drop or rename fields like &lt;code&gt;lines&lt;/code&gt; before they reach the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One agent harness (Copilot CLI) and one graph (codebase-memory-mcp 0.9.0 behind my wrapper). Claude Code, another graph or a newer version could give other numbers.&lt;/li&gt;
&lt;li&gt;Eight questions per cell on the small and medium repositories. With 13 tests under the Holm correction, a cell of eight cannot reach significance even if the graph wins every question, so only the large-repository haiku result is significant.&lt;/li&gt;
&lt;li&gt;Opus answered twelve questions, once each.&lt;/li&gt;
&lt;li&gt;tokenEquiv leaves output tokens out. Counting them would widen haiku's gap on the large repositories: its grep answers there produced a median of 7.0k output tokens against 0.9k with the graph.&lt;/li&gt;
&lt;li&gt;One haiku run failed before Copilot opened a session and is left out. Three sonnet runs failed to fetch Copilot's model list before any model call and were run again. Both are recorded in the plan.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproduce
&lt;/h2&gt;

&lt;p&gt;Everything is in &lt;a href="https://github.com/nefayran/code-graph-vs-grep" rel="noopener noreferrer"&gt;code-graph-vs-grep&lt;/a&gt;: the plan written before the runs, the questions with the evidence for every answer key, the driver, all 664 runs with their answers, and the analysis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node bench/run-copilot.mjs &lt;span class="nt"&gt;--model&lt;/span&gt; claude-haiku-4.5 &lt;span class="nt"&gt;--reps&lt;/span&gt; 3 &lt;span class="nt"&gt;--out&lt;/span&gt; results/v2/claude-haiku-4.5.json
python3 bench/stats-v2.py results/v2/&lt;span class="k"&gt;*&lt;/span&gt;.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ai</category>
      <category>mcp</category>
      <category>llm</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Japan Life Hub: Net Salary Calculator &amp; Housing Guide</title>
      <dc:creator>Rodion Kazennov</dc:creator>
      <pubDate>Thu, 04 Dec 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/nefayran/japan-life-hub-net-salary-calculator-housing-guide-2f95</link>
      <guid>https://dev.to/nefayran/japan-life-hub-net-salary-calculator-housing-guide-2f95</guid>
      <description>&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%2Fdyrcnuhxepgos9zk2c8g.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%2Fdyrcnuhxepgos9zk2c8g.png" alt="Japan Life Hub: Net Salary Calculator &amp;amp; Housing Guide" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just shipped two new features for &lt;a href="https://japan.allkeep.app" rel="noopener noreferrer"&gt;Japan Life Hub&lt;/a&gt; — a Net Salary Calculator with 2025 tax rates and a comprehensive Housing Guide. Both available in English, Japanese, Russian, and Hindi.&lt;/p&gt;

&lt;p&gt;This article covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Year 1 vs Year 2 salary matters&lt;/li&gt;
&lt;li&gt;2025 Japanese tax rates and deductions&lt;/li&gt;
&lt;li&gt;Apartment rental costs breakdown&lt;/li&gt;
&lt;li&gt;Key terminology for foreigners&lt;/li&gt;
&lt;li&gt;Lessons learned building multilingual tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Year 1 Salary Trap
&lt;/h2&gt;

&lt;p&gt;When I moved to Japan, my first paycheck was surprisingly high. Then Year 2 hit.&lt;/p&gt;

&lt;p&gt;The catch: Japan's &lt;strong&gt;resident tax&lt;/strong&gt; (住民税 &lt;em&gt;juuminzei&lt;/em&gt;) is calculated on previous year's income and collected the following year. Your first year in Japan? Zero resident tax. Year 2? Roughly 10% of your gross salary disappears.&lt;/p&gt;

&lt;p&gt;Nobody told me this. I built the calculator I wish I had.&lt;/p&gt;

&lt;h2&gt;
  
  
  Net Salary Calculator
&lt;/h2&gt;

&lt;p&gt;The calculator shows both Year 1 and Year 2 take-home pay side by side.&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%2Fdyrcnuhxepgos9zk2c8g.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%2Fdyrcnuhxepgos9zk2c8g.png" alt="Net Salary Calculator Year 1 vs Year 2" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2025 Tax Rates
&lt;/h3&gt;

&lt;p&gt;Spent a day researching current rates from official sources. Here's what's deducted from your salary:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Social Insurance (Employee Share):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Rate&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Health Insurance&lt;/td&gt;
&lt;td&gt;4.955%&lt;/td&gt;
&lt;td&gt;Tokyo rate, March 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-term Care&lt;/td&gt;
&lt;td&gt;0.795%&lt;/td&gt;
&lt;td&gt;Ages 40-64 only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pension&lt;/td&gt;
&lt;td&gt;9.15%&lt;/td&gt;
&lt;td&gt;Capped at ¥650k/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Employment Insurance&lt;/td&gt;
&lt;td&gt;0.55%&lt;/td&gt;
&lt;td&gt;April 2025 update&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Income Tax:&lt;/strong&gt; Progressive 5-45% across 7 brackets, plus 2.1% reconstruction surcharge until 2037.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resident Tax:&lt;/strong&gt; ~10% (6% prefectural + 4% municipal), delayed one year.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calculator Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Employment type toggle:&lt;/strong&gt; Employee vs Self-employed (different deductions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Age selector:&lt;/strong&gt; Long-term care insurance kicks in at 40&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependents &amp;amp; spouse:&lt;/strong&gt; Affects deductions for employees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual breakdown bar:&lt;/strong&gt; See where your money goes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salary presets:&lt;/strong&gt; Junior ¥3M, Mid ¥5M, Senior ¥7M, Manager ¥10M&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share button:&lt;/strong&gt; Copy summary to clipboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "What You Get Back" section explains what you're paying for — 70% healthcare coverage, pension from 65, unemployment benefits. Japanese social insurance is actually good value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Housing Guide
&lt;/h2&gt;

&lt;p&gt;Finding an apartment in Japan as a foreigner is confusing. Unique fees, guarantor requirements, Japanese-only listings.&lt;/p&gt;

&lt;p&gt;The guide covers the entire process in 8 sections.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Terminology
&lt;/h3&gt;

&lt;p&gt;Japanese rental vocabulary that confused me:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Japanese&lt;/th&gt;
&lt;th&gt;Romaji&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;敷金&lt;/td&gt;
&lt;td&gt;&lt;em&gt;shikikin&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Deposit, 1-2 months, refundable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;礼金&lt;/td&gt;
&lt;td&gt;&lt;em&gt;reikin&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;"Key money" gift to landlord, 0-2 months, gone forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;管理費&lt;/td&gt;
&lt;td&gt;&lt;em&gt;kanrihi&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Monthly maintenance fee, ¥3,000-15,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;更新料&lt;/td&gt;
&lt;td&gt;&lt;em&gt;koushinryo&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Renewal fee, ~1 month every 2 years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;保証会社&lt;/td&gt;
&lt;td&gt;&lt;em&gt;hoshougaisha&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Guarantor company, 30-100% of 1 month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Good news: less than 50% of renters now pay key money. Look for &lt;strong&gt;礼金なし&lt;/strong&gt; (&lt;em&gt;reikin nashi&lt;/em&gt;) listings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Costs Example
&lt;/h3&gt;

&lt;p&gt;For ¥80,000/month rent:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;th&gt;Refundable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;First month rent&lt;/td&gt;
&lt;td&gt;¥80,000&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deposit&lt;/td&gt;
&lt;td&gt;¥80,000&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key money&lt;/td&gt;
&lt;td&gt;¥80,000&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agency fee&lt;/td&gt;
&lt;td&gt;¥88,000&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guarantor&lt;/td&gt;
&lt;td&gt;¥40,000-80,000&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insurance&lt;/td&gt;
&lt;td&gt;¥15,000&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lock change&lt;/td&gt;
&lt;td&gt;¥15,000&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;¥398,000-438,000&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's 5-6x monthly rent upfront. Budget accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to Search
&lt;/h3&gt;

&lt;p&gt;The Housing Guide covers all major platforms — from Japanese-only sites like SUUMO with the largest listings, to English-friendly options like Real Estate Japan and GaijinPot, plus government UR Housing with no key money or guarantor requirements.&lt;/p&gt;

&lt;p&gt;Pro tip: UR Housing has no reikin, no guarantor requirement, no renewal fees. Limited availability but worth checking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timing Matters
&lt;/h3&gt;

&lt;p&gt;Avoid March-April. Everyone moves — new graduates, company transfers. Prices spike, competition fierce.&lt;/p&gt;

&lt;p&gt;Best time: May-February. More negotiation room, landlords eager to fill vacant units.&lt;/p&gt;

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

&lt;p&gt;Japan Life Hub now has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial Costs Calculator&lt;/li&gt;
&lt;li&gt;Net Salary Calculator&lt;/li&gt;
&lt;li&gt;Moving to Japan Guide&lt;/li&gt;
&lt;li&gt;Housing Guide&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Coming soon: Monthly Budget Planner, City Comparison Tool, First Year Timeline.&lt;/p&gt;

&lt;p&gt;The goal is one place for everything you need to know about living in Japan — practical tools, not generic advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://japan.allkeep.app/en/tools/net-salary" rel="noopener noreferrer"&gt;Net Salary Calculator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://japan.allkeep.app/en/guides/housing" rel="noopener noreferrer"&gt;Housing Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://japan.allkeep.app/en/tools" rel="noopener noreferrer"&gt;All Tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built with Next.js, TypeScript, Tailwind CSS. All calculations run client-side, no data collected.&lt;/p&gt;

&lt;p&gt;If you're moving to Japan or know someone who is — share the link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://allkeep.org/en/blog/japan-life-hub-net-salary-housing-guide" rel="noopener noreferrer"&gt;Read on AllKeep →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>japan</category>
      <category>salarycalculator</category>
      <category>housing</category>
      <category>relocation</category>
    </item>
    <item>
      <title>Build a Chrome Extension with React, TypeScript and Vite</title>
      <dc:creator>Rodion Kazennov</dc:creator>
      <pubDate>Wed, 03 Dec 2025 12:13:19 +0000</pubDate>
      <link>https://dev.to/nefayran/build-a-chrome-extension-with-react-typescript-and-vite-31ho</link>
      <guid>https://dev.to/nefayran/build-a-chrome-extension-with-react-typescript-and-vite-31ho</guid>
      <description>&lt;p&gt;This article walks through building a Chrome extension from scratch using React, TypeScript and Vite. We'll build a Freelance Rate Calculator that helps developers check their market rate.&lt;/p&gt;

&lt;p&gt;This article includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup React + Vite + TypeScript project for Chrome Extension&lt;/li&gt;
&lt;li&gt;Configure manifest.json for Manifest V3&lt;/li&gt;
&lt;li&gt;Build calculator UI with Tailwind CSS&lt;/li&gt;
&lt;li&gt;Load and test extension locally&lt;/li&gt;
&lt;li&gt;Submit to Chrome Web Store&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup Project
&lt;/h2&gt;

&lt;p&gt;Create new Vite project with React and TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm create vite@latest rate-calculator -- --template react-ts
cd rate-calculator
npm install

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

&lt;/div&gt;



&lt;p&gt;Add Tailwind CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

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

&lt;/div&gt;



&lt;p&gt;Configure &lt;code&gt;tailwind.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

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

&lt;/div&gt;



&lt;p&gt;Add Tailwind directives to &lt;code&gt;src/index.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@tailwind base;
@tailwind components;
@tailwind utilities;

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

&lt;/div&gt;



&lt;p&gt;Add Lucide icons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install lucide-react

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Manifest V3
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;public/manifest.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "manifest_version": 3,
  "name": "Freelance Rate Calculator",
  "version": "1.0.0",
  "description": "Calculate your freelance rate based on tech stack, experience, and location",
  "action": {
    "default_popup": "index.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

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

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;public/icons/&lt;/code&gt; folder and add extension icons in 16x16, 48x48, and 128x128 sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Rate Data
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;src/data/rates.ts&lt;/code&gt; with tech stacks, experience levels, and locations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface TechStack {
  id: string;
  name: string;
  baseRate: number;
  demand: "high" | "medium" | "low";
}

export interface ExperienceLevel {
  id: string;
  name: string;
  years: string;
  multiplier: number;
}

export interface Location {
  id: string;
  name: string;
  multiplier: number;
}

export const techStacks: TechStack[] = [
  { id: "genai", name: "GenAI / LLM", baseRate: 175, demand: "high" },
  { id: "rust", name: "Rust", baseRate: 135, demand: "high" },
  { id: "react", name: "React", baseRate: 90, demand: "high" },
  { id: "typescript", name: "TypeScript", baseRate: 92, demand: "high" },
  { id: "nodejs", name: "Node.js", baseRate: 85, demand: "high" },
  { id: "python", name: "Python", baseRate: 90, demand: "high" },
  // add more stacks...
];

export const experienceLevels: ExperienceLevel[] = [
  { id: "junior", name: "Junior", years: "0-2 years", multiplier: 0.55 },
  { id: "mid", name: "Mid-level", years: "3-5 years", multiplier: 1.0 },
  { id: "senior", name: "Senior", years: "5-8 years", multiplier: 1.45 },
  { id: "lead", name: "Lead / Architect", years: "8+ years", multiplier: 1.85 },
];

export const locations: Location[] = [
  { id: "usa", name: "United States", multiplier: 1.0 },
  { id: "uk", name: "United Kingdom", multiplier: 0.85 },
  { id: "germany", name: "Germany", multiplier: 0.82 },
  { id: "japan", name: "Japan", multiplier: 0.62 },
  { id: "india", name: "India", multiplier: 0.32 },
  // add more countries...
];

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

&lt;/div&gt;



&lt;p&gt;Rate calculation formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getMarketRate = (
  stack: TechStack,
  exp: ExperienceLevel,
  loc: Location
) =&amp;gt; {
  const base = stack.baseRate * exp.multiplier * loc.multiplier;
  return {
    low: Math.round(base * 0.85),
    mid: Math.round(base),
    high: Math.round(base * 1.15),
  };
};

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

&lt;/div&gt;



&lt;p&gt;Example: Senior React developer in Germany = &lt;code&gt;90 * 1.45 * 0.82 = $107/hr&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Calculator UI
&lt;/h2&gt;

&lt;p&gt;Update &lt;code&gt;src/App.tsx&lt;/code&gt; with calculator form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState } from "react";
import { techStacks, experienceLevels, locations } from "./data/rates";
import type { TechStack, ExperienceLevel, Location } from "./data/rates";

function App() {
  const [stack, setStack] = useState&amp;lt;TechStack | null&amp;gt;(null);
  const [experience, setExperience] = useState&amp;lt;ExperienceLevel | null&amp;gt;(null);
  const [location, setLocation] = useState&amp;lt;Location | null&amp;gt;(null);
  const [myRate, setMyRate] = useState("");
  const [showResult, setShowResult] = useState(false);

  const getMarketRate = () =&amp;gt; {
    if (!stack || !experience || !location) return null;
    const base = stack.baseRate * experience.multiplier * location.multiplier;
    return {
      low: Math.round(base * 0.85),
      mid: Math.round(base),
      high: Math.round(base * 1.15),
    };
  };

  const handleCheck = () =&amp;gt; {
    if (stack &amp;amp;&amp;amp; experience &amp;amp;&amp;amp; location &amp;amp;&amp;amp; myRate) {
      setShowResult(true);
    }
  };

  // render form or results based on showResult state
  return (
    &amp;lt;div className="w-[400px] h-[600px] p-4 bg-gray-900 text-white"&amp;gt;
      {/* form inputs and results */}
    &amp;lt;/div&amp;gt;
  );
}

export default App;

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

&lt;/div&gt;



&lt;p&gt;Set fixed popup dimensions in &lt;code&gt;src/index.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  width: 400px;
  height: 600px;
  margin: 0;
  overflow: hidden;
}

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

&lt;/div&gt;



&lt;p&gt;Input form with dropdowns for stack, experience, location:&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%2Fny6qas5a1kgvk0yy0o2i.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%2Fny6qas5a1kgvk0yy0o2i.png" alt="Rate Calculator Input Form" width="398" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Results screen showing market rate comparison:&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%2Fj4acaul3thd0c263rm67.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%2Fj4acaul3thd0c263rm67.png" alt="Rate Calculator Results" width="393" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Extension
&lt;/h2&gt;

&lt;p&gt;Update &lt;code&gt;vite.config.ts&lt;/code&gt; for extension build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: "dist",
    rollupOptions: {
      input: "index.html",
    },
  },
});

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

&lt;/div&gt;



&lt;p&gt;Build the extension:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;This creates &lt;code&gt;dist/&lt;/code&gt; folder with your extension files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Load Extension Locally
&lt;/h2&gt;

&lt;p&gt;Open Chrome and navigate to &lt;code&gt;chrome://extensions&lt;/code&gt;. Enable "Developer mode" in top right corner.&lt;/p&gt;

&lt;p&gt;Click "Load unpacked" and select the &lt;code&gt;dist/&lt;/code&gt; folder.&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%2Fat60l8nx29x5kktwdb4x.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%2Fat60l8nx29x5kktwdb4x.png" alt="Extension loaded in Chrome" width="454" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the extension icon in toolbar to test the popup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submit to Chrome Web Store
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://chrome.google.com/webstore/devconsole" rel="noopener noreferrer"&gt;Chrome Web Store Developer Dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First time requires $5 one-time developer fee.&lt;/p&gt;

&lt;p&gt;Prepare assets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extension icon: 128x128 PNG&lt;/li&gt;
&lt;li&gt;Screenshot: 1280x800 or 640x400&lt;/li&gt;
&lt;li&gt;Promotional tile: 440x280&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create ZIP of your &lt;code&gt;dist/&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd dist &amp;amp;&amp;amp; zip -r ../extension.zip . &amp;amp;&amp;amp; cd ..

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

&lt;/div&gt;



&lt;p&gt;Upload ZIP, fill in description, add screenshots, submit for review.&lt;/p&gt;

&lt;p&gt;Review takes 1-3 days. Common rejection reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyword stuffing in description&lt;/li&gt;
&lt;li&gt;Missing privacy policy (if using permissions)&lt;/li&gt;
&lt;li&gt;Incorrect screenshots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After approval:&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%2Fq6a83shveo5wk2zcqud3.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%2Fq6a83shveo5wk2zcqud3.png" alt="Extension published in Chrome Web Store" width="800" height="761"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's all. We built a Chrome extension with React, TypeScript and Vite, tested it locally, and published to Chrome Web Store.&lt;/p&gt;

&lt;p&gt;Final stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React 18&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Manifest V3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Live extension: &lt;a href="https://chromewebstore.google.com/detail/freelance-rate-calculator/ofmocfkagkdpbkapmjmobbnomjkiphlf" rel="noopener noreferrer"&gt;Freelance Rate Calculator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you liked the article, follow me on &lt;a href="https://twitter.com/Nefayran" rel="noopener noreferrer"&gt;Twitter @Nefayran&lt;/a&gt; for more #buildinpublic content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://allkeep.org/en/blog/build-chrome-extension-react-typescript-vite" rel="noopener noreferrer"&gt;Read on AllKeep →&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
      <category>freelance</category>
    </item>
  </channel>
</rss>
