<?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: Meursyphus</title>
    <description>The latest articles on DEV Community by Meursyphus (@moondaeseung).</description>
    <link>https://dev.to/moondaeseung</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%2F940053%2F46f5a053-e7f2-4aeb-bde9-053f4d70efe4.png</url>
      <title>DEV Community: Meursyphus</title>
      <link>https://dev.to/moondaeseung</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/moondaeseung"/>
    <language>en</language>
    <item>
      <title>My AI Tests Passed While Half the Mutants Went Undetected</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Tue, 10 Nov 2026 00:34:17 +0000</pubDate>
      <link>https://dev.to/moondaeseung/my-ai-tests-passed-while-half-the-mutants-went-undetected-31p</link>
      <guid>https://dev.to/moondaeseung/my-ai-tests-passed-while-half-the-mutants-went-undetected-31p</guid>
      <description>&lt;p&gt;If someone asked me how I verified code written by AI, I used to have a simple answer: I ran the tests.&lt;/p&gt;

&lt;p&gt;My project had 451 tests across 84 files. Claude Code had written them, and I had reviewed and merged them. A green test run felt reassuring.&lt;/p&gt;

&lt;p&gt;Then a friend mentioned mutation testing. I tried it on ten files in &lt;code&gt;src/lib&lt;/code&gt; that already had tests. The tool generated 550 small changes to the production code and checked whether the tests noticed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Mutants&lt;/th&gt;
&lt;th&gt;Killed&lt;/th&gt;
&lt;th&gt;Survived&lt;/th&gt;
&lt;th&gt;No coverage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ten tested files in &lt;code&gt;src/lib&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;550&lt;/td&gt;
&lt;td&gt;273&lt;/td&gt;
&lt;td&gt;161&lt;/td&gt;
&lt;td&gt;116&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The tests caught 49.6% of those mutants. Another 161 survived despite running tests, and 116 had no test coverage.&lt;/p&gt;

&lt;p&gt;That does not mean half of my 451 tests were worthless. It means roughly half of the generated mutations in this sample went undetected. That was enough to make me inspect what the tests actually asserted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some tests only checked the source text
&lt;/h2&gt;

&lt;p&gt;Here is an excerpt from my project, with the test description translated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relativePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="no"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`../${relativePath}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'utf8'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'separates new time credits from the legacy user baseline'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;codingTimeRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'src/server/repository/coding-time-credit/index.ts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codingTimeRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'userCodingTimeCreditLedger'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codingTimeRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'projectId'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reads a file as a string and checks for words. It does not execute the credit calculation or verify its result.&lt;/p&gt;

&lt;p&gt;I found 62 tests like this across seven files. They can enforce a convention, but they cannot establish that the program behaves correctly. The implementation could return the wrong value while those checks stay green.&lt;/p&gt;

&lt;p&gt;Other patterns deserved a closer look too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copying the implementation's current output directly into the expected value, without checking whether that output matches the requirement.&lt;/li&gt;
&lt;li&gt;Reimplementing the production algorithm inside the test to calculate the expected result.&lt;/li&gt;
&lt;li&gt;Checking only that a mock was called. My project had 139 &lt;code&gt;toHaveBeenCalled()&lt;/code&gt; assertions; the useful question was what each one established about behavior.&lt;/li&gt;
&lt;li&gt;Treating test-file count or coverage as proof that important decisions have assertions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is unique to AI. But when the same agent writes the implementation and its tests in one session, both can inherit the same misunderstanding.&lt;/p&gt;

&lt;p&gt;A 2026 study, &lt;em&gt;All Smoke, No Alarm&lt;/em&gt;, examined 86,156 test-file patches from 33,596 agent-authored pull requests. Its syntactic classification found weak or absent explicit oracle signals in 80.2% of patches. That is a result about observable assertion patterns in that dataset, not proof that 80.2% of all AI tests are useless. It does reinforce why counting tests is a poor substitute for inspecting their checks. &lt;a href="https://arxiv.org/html/2606.18168v1" rel="noopener noreferrer"&gt;Study&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutation testing checks whether a change makes a test fail
&lt;/h2&gt;

&lt;p&gt;A mutation tool changes the production code in small ways: replacing &lt;code&gt;&amp;lt;&lt;/code&gt; with &lt;code&gt;&amp;lt;=&lt;/code&gt;, changing &lt;code&gt;+&lt;/code&gt; to &lt;code&gt;-&lt;/code&gt;, forcing a condition to &lt;code&gt;false&lt;/code&gt;, or replacing a return value.&lt;/p&gt;

&lt;p&gt;Each modified version is a &lt;strong&gt;mutant&lt;/strong&gt;. The tool runs the relevant tests against it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Result&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;Killed&lt;/td&gt;
&lt;td&gt;At least one test failed with the mutation active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Survived&lt;/td&gt;
&lt;td&gt;The tests still passed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No coverage&lt;/td&gt;
&lt;td&gt;No test covered the mutated code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are the three results I focused on. Tools also report timeouts, invalid mutations, and other states. Stryker's overall score counts detected mutants against valid mutants; its covered-code score uses a different denominator. &lt;a href="https://stryker-mutator.io/docs/mutation-testing-elements/mutant-states-and-metrics/" rel="noopener noreferrer"&gt;Stryker states and metrics&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isUserOldEnough&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a mutant changes &lt;code&gt;&amp;gt;= 18&lt;/code&gt; to &lt;code&gt;&amp;gt; 18&lt;/code&gt;, tests using ages 17 and 19 still pass. A test for exactly 18 can distinguish the two implementations.&lt;/p&gt;

&lt;p&gt;Coverage can tell me that a test executed the comparison. Mutation testing helps me find whether the assertion notices a meaningful change to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eleven tests missed six boundary changes
&lt;/h2&gt;

&lt;p&gt;My first run used StrykerJS with Vitest in a Next.js project. The configuration was small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;"testRunner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"vitest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"@stryker-mutator/vitest-runner"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"mutate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src/lib/**/*.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"disableTypeChecks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"incremental"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"reporters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"clear-text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Stryker and its Vitest runner installed and the test configuration available, I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx stryker run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That initial run took 1 minute and 5 seconds in my project.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;disableTypeChecks: false&lt;/code&gt; setting kept Stryker from inserting its usual &lt;code&gt;@ts-nocheck&lt;/code&gt; directives. It did not make Vitest perform TypeScript checking; type validation still needs its own checker or command. &lt;a href="https://stryker-mutator.io/docs/stryker-js/configuration/#disabletypechecks-boolean--string" rel="noopener noreferrer"&gt;Stryker configuration&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most memorable result came from &lt;code&gt;kstRelativeTime&lt;/code&gt;, a utility that produces labels such as “just now,” “three hours ago,” and “yesterday.” Eleven tests exercised it. All six mutants that changed &lt;code&gt;&amp;lt;&lt;/code&gt; to &lt;code&gt;&amp;lt;=&lt;/code&gt; survived.&lt;/p&gt;

&lt;p&gt;I had tests around the date boundaries, but not at the boundaries. None asked what should happen at exactly 48 hours. A branch that produced a number of weeks had no coverage at all.&lt;/p&gt;

&lt;p&gt;Other results were harder to ignore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing &lt;code&gt;+ 1&lt;/code&gt; to &lt;code&gt;- 1&lt;/code&gt; in a sequence number used for concurrent editing still passed.&lt;/li&gt;
&lt;li&gt;Replacing &lt;code&gt;if (local === remote)&lt;/code&gt; with &lt;code&gt;if (false)&lt;/code&gt; in text-merging logic still passed.&lt;/li&gt;
&lt;li&gt;The Markdown image parser killed only six out of 109 mutants.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of those files already had tests. The CI result was green.&lt;/p&gt;

&lt;p&gt;The source-text tests caused a different problem. Instrumentation changes the source file, so tests that inspect its literal text can fail because the tool inserted scaffolding. I excluded those tests from the mutation run. I wanted failures caused by changed behavior, not by the instrumentation's spelling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters when agents write tests
&lt;/h2&gt;

&lt;p&gt;Tests give a coding agent feedback. Weak assertions can tell the agent that it has finished even when an important behavior remains unchecked.&lt;/p&gt;

&lt;p&gt;Mutation testing provides a more specific follow-up than “write more tests.” I can point to a surviving change and ask what input would distinguish the original behavior from the mutated behavior. Then I can review whether that expected result actually follows from the requirement.&lt;/p&gt;

&lt;p&gt;Meta's Automated Compliance Hardener uses a related approach at a different scale. It generates concern-specific mutants with an LLM, then generates tests to catch faults that the existing suite misses. The reported deployment produced 9,095 mutants and 571 tests; engineers accepted 73% of tests in its Messenger and WhatsApp test-a-thons. The useful idea is that a new test can improve fault detection even when it exercises lines that existing tests already cover. &lt;a href="https://arxiv.org/html/2501.12862v1" rel="noopener noreferrer"&gt;Meta paper&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent can help improve the tests, but I still need to decide what the program should do. Otherwise it can simply encode the same wrong assumption more thoroughly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the feedback close to the change
&lt;/h2&gt;

&lt;p&gt;The obvious concern is runtime. A naive process that runs a five-minute suite for each of 1,000 mutants would take about 83 hours.&lt;/p&gt;

&lt;p&gt;Modern tools avoid much of that repetition. Stryker can select tests using per-test coverage, and incremental mode reuses previous results where appropriate. That makes a small run on relevant logic a different proposition from mutating an entire repository every time. &lt;a href="https://stryker-mutator.io/docs/stryker-js/configuration/" rel="noopener noreferrer"&gt;Stryker configuration&lt;/a&gt;, &lt;a href="https://stryker-mutator.io/docs/stryker-js/incremental/" rel="noopener noreferrer"&gt;incremental mode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Comwit and its project template, I adopted a few practical rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with application logic: utilities, server logic, APIs, and state. I excluded pages, routing, UI components, and database schemas from this particular setup.&lt;/li&gt;
&lt;li&gt;Run mutation tests on changed files during normal work. Use a broader run occasionally to establish a baseline.&lt;/li&gt;
&lt;li&gt;When an agent changes tests, have it run the changed-file mutation command and inspect survivors.&lt;/li&gt;
&lt;li&gt;Review survivors by cause. A boundary change suggests an exact-value test; a skipped branch suggests a missing input path. Equivalent behavior or irrelevant logging changes need a different decision.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The command in my repository is &lt;code&gt;pnpm test:mutation:changed&lt;/code&gt;. It is a project-specific script, not a built-in Stryker command.&lt;/p&gt;

&lt;p&gt;I also ran a larger baseline: 902 logic files, 41,343 mutants, and about 49 minutes in my environment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Total mutants&lt;/th&gt;
&lt;th&gt;Covered mutants&lt;/th&gt;
&lt;th&gt;Survived&lt;/th&gt;
&lt;th&gt;Score on covered mutants&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;All targeted logic&lt;/td&gt;
&lt;td&gt;41,343&lt;/td&gt;
&lt;td&gt;10,230&lt;/td&gt;
&lt;td&gt;3,799&lt;/td&gt;
&lt;td&gt;62.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/server/repository&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;12,516&lt;/td&gt;
&lt;td&gt;5,121&lt;/td&gt;
&lt;td&gt;2,021&lt;/td&gt;
&lt;td&gt;60.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/server/domain-jobs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2,490&lt;/td&gt;
&lt;td&gt;1,084&lt;/td&gt;
&lt;td&gt;459&lt;/td&gt;
&lt;td&gt;57.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The denominator matters. The 62.9% figure describes the covered mutants, not all 41,343 generated mutants. Only about a quarter of the generated mutants had coverage, and more than a third of those covered mutants survived.&lt;/p&gt;

&lt;p&gt;Those numbers gave me places to investigate. They did not give me a reliable estimate of how many production bugs the application had.&lt;/p&gt;

&lt;h2&gt;
  
  
  The score is a prompt to investigate
&lt;/h2&gt;

&lt;p&gt;A surviving mutant is not automatically a missing requirement. Some mutations leave observable behavior unchanged. Those equivalent mutants cannot be killed by a test that correctly checks the same behavior.&lt;/p&gt;

&lt;p&gt;Mutation testing also cannot prove that the implementation matches the intended specification. It checks sensitivity to the generated changes. A test suite and implementation can agree with each other and still agree on the wrong behavior.&lt;/p&gt;

&lt;p&gt;That is why I do not want the mutation score to become a team KPI. It would be easy to optimize the number and miss the reason for running the tool.&lt;/p&gt;

&lt;p&gt;I still ask AI to write tests. What changed is the next question I ask: &lt;strong&gt;if this code were slightly wrong, which test would notice?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my first run, one minute of mutation testing gave me a much more useful review queue than another reassuring green check.&lt;/p&gt;




&lt;p&gt;I build &lt;a href="https://comwit.io" rel="noopener noreferrer"&gt;Comwit&lt;/a&gt;, where this workflow is part of the project template. Adapted from my &lt;a href="https://velog.io/@k-svelte-master/ai-generated-tests-mutation-testing" rel="noopener noreferrer"&gt;original Korean article on Velog&lt;/a&gt;, published October 7, 2026.&lt;/p&gt;

&lt;p&gt;Contact: &lt;a href="https://x.com/meursyphus" rel="noopener noreferrer"&gt;X @meursyphus&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Why Next.js Navigation Feels Slow Even When the Server Is Fast</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Tue, 10 Nov 2026 00:30:32 +0000</pubDate>
      <link>https://dev.to/moondaeseung/why-nextjs-navigation-feels-slow-even-when-the-server-is-fast-4kop</link>
      <guid>https://dev.to/moondaeseung/why-nextjs-navigation-feels-slow-even-when-the-server-is-fast-4kop</guid>
      <description>&lt;p&gt;I used Next.js for a while before I could explain why clicking a link sometimes did nothing for a moment.&lt;/p&gt;

&lt;p&gt;The server was not doing anything especially expensive. The browser already had plenty of JavaScript. Yet the next screen still waited for a network round trip.&lt;/p&gt;

&lt;p&gt;The missing piece was the React Server Component payload for the destination route. Understanding when Next.js fetches that payload, and which parts it can reuse, made the delay much easier to explain and fix.&lt;/p&gt;

&lt;p&gt;This is the first article in my Next.js performance series. The examples cover the App Router, the older route-level caching model, and the &lt;code&gt;cacheComponents&lt;/code&gt; and &lt;code&gt;partialPrefetching&lt;/code&gt; options available in Next.js 16.3 and later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The browser needs more than JavaScript
&lt;/h2&gt;

&lt;p&gt;In a client-rendered application, the browser can render a destination screen as soon as it has the relevant JavaScript. Data may still be loading, but the application can show its own loading UI immediately.&lt;/p&gt;

&lt;p&gt;Server Components change what the browser has available. Their implementation stays on the server. The browser receives their rendered output through the RSC payload, alongside references to Client Components and the props those components need.&lt;/p&gt;

&lt;p&gt;That payload is a serialized representation of the component tree. It is not simply an HTML document, and it is not the source code of the Server Components.&lt;/p&gt;

&lt;p&gt;Here is a small example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/posts/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;LikeButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'./like-button'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;PostsPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Posts&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LikeButton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/posts/like-button.tsx&lt;/span&gt;
&lt;span class="s1"&gt;'use client'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'react'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;LikeButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;liked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setLiked&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLiked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;liked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;liked&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'Liked'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Like'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PostsPage&lt;/code&gt; is a Server Component even though it has no &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt;, or database query. App Router pages and layouts are Server Components by default. The &lt;code&gt;'use client'&lt;/code&gt; directive establishes a client boundary; components imported through that boundary belong to the client module graph.&lt;/p&gt;

&lt;p&gt;For navigation, I find it useful to imagine that the browser has part of the destination tree, with spaces that need the server's output. The important question is whether that output arrives &lt;strong&gt;before or after the click&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the required payload is missing, navigation may wait for the server. A prefetched loading boundary or static shell can still provide immediate feedback, so the presence of a Server Component alone does not make every transition slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Link prefetching actually does
&lt;/h2&gt;

&lt;p&gt;Next.js can fetch route data before navigation. In production, a &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; entering the viewport normally triggers prefetching. The browser can then reuse the payload when the user clicks. Automatic prefetching is disabled in development, so &lt;code&gt;next dev&lt;/code&gt; is not a useful way to judge its production behavior.&lt;/p&gt;

&lt;p&gt;Before enabling Partial Prefetching, the default behavior differs by route:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Default prefetch behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Statically rendered route&lt;/td&gt;
&lt;td&gt;Prefetch the full route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamically rendered route&lt;/td&gt;
&lt;td&gt;Prefetch down to the nearest &lt;code&gt;loading.js&lt;/code&gt; boundary, when available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;prefetch={true}&lt;/code&gt; requests a fuller prefetch, including for dynamic routes under the older model. It can reduce the wait after a click, but it also does more server work before the user decides to visit. With Partial Prefetching enabled, the rules change again: default prefetching focuses on the shared App Shell. &lt;a href="https://nextjs.org/docs/app/api-reference/components/link#prefetch" rel="noopener noreferrer"&gt;Next.js Link reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The basic sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Link becomes visible
  → fetch reusable route content
  → keep it in the browser's Client Cache

User clicks
  → render the cached content
  → request anything still missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moving work before the click can matter more to perceived speed than shaving a few milliseconds off the server render.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic segments and dynamic rendering are different
&lt;/h2&gt;

&lt;p&gt;The word “dynamic” causes a lot of confusion here.&lt;/p&gt;

&lt;p&gt;A folder such as &lt;code&gt;posts/[slug]&lt;/code&gt; defines a &lt;strong&gt;dynamic URL segment&lt;/strong&gt;. It does not, by itself, mean that every visit requires request-time rendering. &lt;code&gt;generateStaticParams&lt;/code&gt; can provide values that Next.js prerenders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic rendering&lt;/strong&gt; means rendering at request time. In the previous caching model, reading request-specific values such as &lt;code&gt;cookies()&lt;/code&gt; or &lt;code&gt;headers()&lt;/code&gt; opts a route into dynamic rendering. That can happen deep inside a component tree.&lt;/p&gt;

&lt;p&gt;Imagine a mostly static article page with a small personalized control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Article page
├─ Article title
├─ Article body
└─ Personal controls
   └─ reads cookies()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without Cache Components or another partial-prerendering setup, that request-specific read affects the route's rendering mode. The title and body may be easy to render, but the route no longer has one completely static response that can serve every request. Cached data and shared layouts can still be reused; “dynamic” does not mean every cache disappears. &lt;a href="https://nextjs.org/docs/app/guides/caching-without-cache-components" rel="noopener noreferrer"&gt;Next.js previous caching model&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Suspense helps only when its fallback is available
&lt;/h2&gt;

&lt;p&gt;Wrapping the personalized control in &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; lets the server stream the surrounding content and fallback before the slow part finishes.&lt;/p&gt;

&lt;p&gt;But in the previous model, an ordinary Suspense boundary inside a dynamic page does not automatically turn everything around it into a build-time shell. The browser may still need the first server response before it knows what to render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Click
  → request the destination payload
  → first response arrives
  → show surrounding content and fallback
  → stream the remaining content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A prefetched &lt;code&gt;loading.tsx&lt;/code&gt; boundary can make the initial transition feel immediate. A fallback that exists only in an unfetched destination payload cannot do that yet. This is the distinction I was missing when I thought, “I already use Suspense, so why does navigation still pause?” &lt;a href="https://nextjs.org/docs/app/guides/instant-navigation" rel="noopener noreferrer"&gt;Next.js instant navigation guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Network distance makes the difference obvious. While hosting one of my services on Cloudflare, I sometimes saw traffic from Korea reach Los Angeles or Hong Kong. Even a quick server render could not eliminate that round trip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read staleTimes as page reuse rules
&lt;/h2&gt;

&lt;p&gt;The experimental &lt;code&gt;staleTimes&lt;/code&gt; configuration controls reuse of page segments in the Client Cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Defaults in the previous caching model&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;staleTimes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;nextConfig&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The names are easy to read too literally. The &lt;code&gt;dynamic&lt;/code&gt; interval applies to pages that are neither statically generated nor fully prefetched. The &lt;code&gt;static&lt;/code&gt; interval also applies to fully prefetched pages. Loading boundaries use the static interval, shared layouts can remain reusable, and browser back/forward navigation has its own caching behavior.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;dynamic: 0&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; mean “Next.js never keeps anything from a dynamic route.” It explains one particular reason an ordinary revisit may request the page segment again. The default changed from 30 seconds to zero in Next.js 15. &lt;a href="https://nextjs.org/docs/app/api-reference/config/next-config-js/staleTimes" rel="noopener noreferrer"&gt;Next.js staleTimes reference&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache Components gives the page a reusable shell
&lt;/h2&gt;

&lt;p&gt;With Cache Components, Next.js can prerender a shell while leaving request-specific content behind Suspense boundaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;cacheComponents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;nextConfig&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives us a different navigation sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before the click
  → prefetch the static shell and its fallbacks

After the click
  → show that shell immediately
  → stream request-specific content into it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I now place a boundary around the part that actually needs request-time information, rather than assuming the whole page must wait.&lt;/p&gt;

&lt;p&gt;Enabling the flag also changes how the application expresses caching. Reusable work can use &lt;code&gt;'use cache'&lt;/code&gt;; uncached work that should stream needs an appropriate Suspense boundary. Existing applications may need more than a configuration change to adopt this model. &lt;a href="https://nextjs.org/docs/app/getting-started/caching" rel="noopener noreferrer"&gt;Next.js caching guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I learned that through an awkward migration. With Next.js 16.2 on Cloudflare/OpenNext, I turned the option on and off while working through middleware compatibility, authentication reads in layouts, and transition behavior. I eventually enabled it after changing hosting. That experience made me treat this as an application migration, not a free performance switch.&lt;/p&gt;

&lt;p&gt;There is also a lifecycle detail to check. Next.js can preserve recently visited screens with React Activity. Activity hides DOM and preserves state while cleaning up Effects. Code that assumes leaving a route always destroys its DOM may need attention, particularly media elements and other work tied to the DOM itself. &lt;a href="https://react.dev/reference/react/Activity" rel="noopener noreferrer"&gt;React Activity reference&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Partial Prefetching shares the shell across URLs
&lt;/h2&gt;

&lt;p&gt;Next.js 16.3 introduced another opt-in flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;cacheComponents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;partialPrefetching&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;nextConfig&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An article list might link to &lt;code&gt;/posts/1&lt;/code&gt;, &lt;code&gt;/posts/2&lt;/code&gt;, and &lt;code&gt;/posts/3&lt;/code&gt;. Those URLs can share the same structural shell even though the article content differs. Partial Prefetching lets Next.js reuse that App Shell across links to the same route pattern, instead of treating each URL as a completely separate prefetch target. &lt;a href="https://nextjs.org/blog/next-16-3" rel="noopener noreferrer"&gt;Next.js 16.3 announcement&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where we read URL-specific information matters. Awaiting &lt;code&gt;params&lt;/code&gt; at the top of a page ties more of the render to that particular URL. Passing the promise down and reading it inside a Suspense boundary leaves more URL-independent UI in the shared shell.&lt;/p&gt;

&lt;p&gt;The default prefetch then brings in the shell; URL-specific content can stream after navigation. If a particular link deserves more eager work, &lt;code&gt;prefetch={true}&lt;/code&gt; can also prefetch cached content that depends on its URL. It is a tradeoff between earlier work and faster transitions. &lt;a href="https://nextjs.org/docs/app/guides/adopting-partial-prefetching" rel="noopener noreferrer"&gt;Adopting Partial Prefetching&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Service workers need a separate caching design
&lt;/h2&gt;

&lt;p&gt;I also use Serwist to persist selected resources beyond the browser's in-memory route cache. Static assets are an obvious starting point. Persisting RSC responses is much more sensitive because their representation can depend on request headers, route state, deployment, and authentication.&lt;/p&gt;

&lt;p&gt;My original implementation used Next.js response headers to distinguish responses and applied custom cache rules. I would not treat that snippet as a general recipe for other applications. Removing &lt;code&gt;_rsc&lt;/code&gt; from cache keys or caching personalized responses without preserving the relevant distinctions can reuse the wrong response.&lt;/p&gt;

&lt;p&gt;For most applications, I would first get framework prefetching and the App Shell right, then add a service worker only for a specific offline or persistence requirement. &lt;a href="https://nextjs.org/docs/app/guides/cdn-caching" rel="noopener noreferrer"&gt;Next.js CDN caching guide&lt;/a&gt;, &lt;a href="https://serwist.pages.dev/" rel="noopener noreferrer"&gt;Serwist&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A list already contains part of the detail page
&lt;/h2&gt;

&lt;p&gt;The last improvement in my application came from the screen design.&lt;/p&gt;

&lt;p&gt;When someone opens an item from a list, the browser already knows its title, thumbnail, and summary. Replacing all of that with a full-page spinner throws away useful information.&lt;/p&gt;

&lt;p&gt;For the showcase in my service, I use Intercepting Routes to distinguish two ways of reaching the same URL:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entry path&lt;/th&gt;
&lt;th&gt;What the user sees&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Navigation from the list&lt;/td&gt;
&lt;td&gt;Show the cached summary immediately, then fetch the remaining detail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direct visit or refresh&lt;/td&gt;
&lt;td&gt;Render the full detail through the normal server route&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The intercepted route uses the list's existing client state as initial content. If there is no matching summary, it falls back to a skeleton. The regular route handles server data loading and metadata for direct visits.&lt;/p&gt;

&lt;p&gt;Intercepting Routes provide the routing distinction; the shared client state provides the instant content. They do not, by themselves, guarantee a navigation without any RSC request. &lt;a href="https://nextjs.org/docs/app/api-reference/file-conventions/intercepting-routes" rel="noopener noreferrer"&gt;Next.js Intercepting Routes&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the delay after the click
&lt;/h2&gt;

&lt;p&gt;I still like Next.js. These changes came from using it in &lt;a href="https://comwit.io" rel="noopener noreferrer"&gt;Comwit&lt;/a&gt;, the service I build, and wanting the navigation to feel better.&lt;/p&gt;

&lt;p&gt;When a transition feels slow, I now look at three things: what the browser already has, what it still needs from the server, and what it can show while that request is in flight.&lt;/p&gt;

&lt;p&gt;A Server Component does not automatically make navigation slow. Waiting until the click to obtain the UI needed for the next screen is what I look for first.&lt;/p&gt;




&lt;p&gt;Adapted from my &lt;a href="https://velog.io/@k-svelte-master/nextjs-slow-1-dynamic-rsc-prefetch" rel="noopener noreferrer"&gt;original Korean article on Velog&lt;/a&gt;, published September 27, 2026.&lt;/p&gt;

&lt;p&gt;Contact: &lt;a href="https://x.com/meursyphus" rel="noopener noreferrer"&gt;X @meursyphus&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>performance</category>
    </item>
    <item>
      <title>I made awesome react apps with transition aniamtion</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Mon, 04 Aug 2025 03:08:08 +0000</pubDate>
      <link>https://dev.to/moondaeseung/i-made-awesome-react-apps-with-transition-aniamtion-286a</link>
      <guid>https://dev.to/moondaeseung/i-made-awesome-react-apps-with-transition-aniamtion-286a</guid>
      <description>&lt;p&gt;&lt;a href="https://ssgoi.dev/demo" rel="noopener noreferrer"&gt;https://ssgoi.dev/demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;you can see it on mobile phone. It makes animation when navigating other pages&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>🚨 Still Manually Handling Complex Web Graphics?</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Sat, 21 Jun 2025 16:27:06 +0000</pubDate>
      <link>https://dev.to/moondaeseung/still-manually-handling-complex-web-graphics-1gno</link>
      <guid>https://dev.to/moondaeseung/still-manually-handling-complex-web-graphics-1gno</guid>
      <description>&lt;p&gt;Are you still wrestling with manual calculations, text wrapping, drag-and-drop events, and endless revisions when handling complex web graphics? Are hundreds of lines of code driving you crazy every time there's a small change?&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;Flitter&lt;/strong&gt;(&lt;a href="https://flitter.dev" rel="noopener noreferrer"&gt;https://flitter.dev&lt;/a&gt;), your game-changing solution!&lt;/p&gt;




&lt;h2&gt;
  
  
  😫 Everyday Developer Struggles, Familiar?
&lt;/h2&gt;

&lt;p&gt;Imagine the frequent scenario at a startup meeting:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We need a workflow editor, something like Figma—nodes you can drag and connect."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Developers panic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Using D3.js, that's easily three weeks of work..."&lt;/li&gt;
&lt;li&gt;"SVG? Canvas? How will we handle automatic text wrapping?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Say goodbye to these headaches with Flitter!&lt;/p&gt;




&lt;h2&gt;
  
  
  🥳 Flitter Magic: Just a Few Lines of Code
&lt;/h2&gt;

&lt;p&gt;Long text wrapping and centering seamlessly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fflitter.dev%2Fauto-wrap.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fflitter.dev%2Fauto-wrap.gif" alt="Auto Wrap Demo" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the only code you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Long text automatically wraps and centers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TextAlign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Declarative code—clear and intuitive&lt;/li&gt;
&lt;li&gt;Automatic cursor handling and drag interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No more coordinate calculations!&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 Unique Advantages of Flitter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🚀 Dual Rendering System (SVG + Canvas)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy36snr83eie7pflw6ofw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy36snr83eie7pflw6ofw.gif" alt="SVG vs Canvas" width="799" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SVG&lt;/strong&gt;: SEO-friendly, selectable text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canvas&lt;/strong&gt;: High performance, smooth interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💡 Server-side Rendering + Canvas Hydration
&lt;/h3&gt;

&lt;p&gt;Rapid initial load with smooth interactions right from the start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Widget&lt;/span&gt;
  &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"canvas"&lt;/span&gt;
  &lt;span class="n"&gt;ssr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ❌ Still Using Other Libraries?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Issues&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;D3.js&lt;/td&gt;
&lt;td&gt;Manual calculations (coordinates, dimensions…)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Konva.js&lt;/td&gt;
&lt;td&gt;Difficult complex layouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fabric.js&lt;/td&gt;
&lt;td&gt;Limited UI capabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paper.js&lt;/td&gt;
&lt;td&gt;Only suitable for vector graphics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Three.js&lt;/td&gt;
&lt;td&gt;Overkill for 2D UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🎯 Flitter Simplifies Everything!
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;headerStyle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EdgeInsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BoxDecoration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'#F3F4F6'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BorderRadius&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;circular&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mainAxisAlignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MainAxisAlignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spaceBetween&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="cm"&gt;/* Easily implemented layouts */&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  👨‍💻 Flutter Developers, You're Already Experts!
&lt;/h2&gt;

&lt;p&gt;Flitter shares nearly identical syntax with Flutter—instant onboarding!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Flutter code&lt;/span&gt;
&lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EdgeInsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello Flutter"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Flitter code&lt;/span&gt;
&lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EdgeInsets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello Flitter"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎖️ Proven in Real-world Projects
&lt;/h2&gt;

&lt;h3&gt;
  
  
  📌 EasyRD – ERD Diagram Editor
&lt;/h3&gt;

&lt;p&gt;Seamlessly handle over 100 nodes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0mic7m9jzau9bv0w2xo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0mic7m9jzau9bv0w2xo.gif" alt="EasyRD" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/meursyphus/easy-rd" rel="noopener noreferrer"&gt;Visit on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📌 Headless Chart – Customizable Charts
&lt;/h3&gt;

&lt;p&gt;Easier than D3.js, more flexible than Chart.js.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbcrst8ejz0bijp6sgke.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzbcrst8ejz0bijp6sgke.gif" alt="Headless Chart" width="720" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/meursyphus/headless-chart" rel="noopener noreferrer"&gt;Visit on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://headless-chart.pages.dev/" rel="noopener noreferrer"&gt;Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚩 Who Should Use Flitter?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SaaS teams regularly developing complex UIs&lt;/li&gt;
&lt;li&gt;Teams frequently building node editors and workflows&lt;/li&gt;
&lt;li&gt;Data visualization teams needing custom charts&lt;/li&gt;
&lt;li&gt;Teams tired of complexity in existing graphic libraries&lt;/li&gt;
&lt;li&gt;Teams with Flutter developers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Get Started Right Now!
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm install @meursyphus/flitter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just one command to start!&lt;/p&gt;

&lt;p&gt;Check out the next page for detailed usage and examples.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>"Performance Has Improved!" Can You Really Prove It?</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Tue, 10 Dec 2024 12:25:31 +0000</pubDate>
      <link>https://dev.to/moondaeseung/performance-has-improved-can-you-really-prove-it-10mn</link>
      <guid>https://dev.to/moondaeseung/performance-has-improved-can-you-really-prove-it-10mn</guid>
      <description>&lt;p&gt;Every frontend developer has experienced this situation:&lt;br&gt;
"I optimized this part, and it definitely feels faster!"&lt;br&gt;
"Hmm... I don't really notice much difference in my environment."&lt;/p&gt;

&lt;p&gt;When you open Chrome DevTools and try to measure performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The hassle of opening the tools and clicking Record every time&lt;/li&gt;
&lt;li&gt;Measurement environments vary with browser cache, CPU state, network conditions&lt;/li&gt;
&lt;li&gt;Sharing performance improvement results with colleagues through screenshots&lt;/li&gt;
&lt;li&gt;Difficulty tracking whether performance degrades over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a result, we end up saying performance has improved based on "feeling."&lt;br&gt;
However, true performance optimization should be provable with objective metrics.&lt;/p&gt;

&lt;p&gt;In this article, we'll explore automated performance measurement methods to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collect objective performance data in a consistent environment&lt;/li&gt;
&lt;li&gt;Track performance changes over time&lt;/li&gt;
&lt;li&gt;Establish performance metrics that the entire team can share&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Pro Tip&lt;/strong&gt;: While Chrome DevTools' Performance panel is a powerful tool, it has limitations with manual measurement. Through automation, we can overcome these limitations while still utilizing DevTools' analysis capabilities.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Scientific Experimentation: Reliable Performance Measurement Methods
&lt;/h1&gt;

&lt;p&gt;Did you know you can control Chrome DevTools' Performance panel programmatically?&lt;br&gt;
Using Playwright, you can automate performance measurements through browser automation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started with Automated Performance Measurement Using Playwright
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Capture performance traces'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Start Chrome trace recording&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`./traces/${formatDate(new Date())}.json`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Load page and perform actions&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://localhost:4173/performance-test'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'button#start'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Record performance measurement points&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Perf:Started'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Perf:Ended'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'overall'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Perf:Started'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Perf:Ended'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// End measurement and save results&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;However, measuring just once isn't enough.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;A Habit from Physics Lab Days&lt;/strong&gt;&lt;br&gt;
"Remember, everyone! One measurement is no measurement!"&lt;/p&gt;

&lt;p&gt;This is a habit ingrained from writing physics lab reports. 😄&lt;br&gt;
I still vividly remember that measurement error is inversely proportional&lt;br&gt;
to the square root of the number of repeated measurements.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Scientific Approach to Performance Measurement
&lt;/h2&gt;

&lt;p&gt;Applying principles learned from physics experiments to web performance measurement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Importance of Repeated Measurements&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard error decreases by 1/√n as the number of measurements (n) increases&lt;/li&gt;
&lt;li&gt;With 50 repetitions, error rate reduces to about 1/7 compared to a single measurement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Strict Control of Variables&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just like controlling temperature and humidity in a lab&lt;/li&gt;
&lt;li&gt;Essential to control browser cache, memory state, CPU load&lt;/li&gt;
&lt;li&gt;Must consider V8 engine's JIT compiler optimizations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Complete Code for Reliable Measurements
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Scientifically measure performance'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="no"&gt;REPEAT_COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Sufficient repetitions for reliability&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;REPEAT_COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Control variables: fresh browser environment each time&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'--no-sandbox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'--disable-dev-shm-usage'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`./traces/trace_${i}.json`&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Performance measurement&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;measurePerformance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Reset browser state&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Statistical analysis of results&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;calculateStats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`
        Average: ${stats.mean}ms
        Standard Deviation: ${stats.stdDev}ms
        95% Confidence Interval: ${stats.confidenceInterval}ms
    `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The Pitfall of JIT Compiler: Code Gets Faster with Execution
&lt;/h2&gt;

&lt;p&gt;Here's an interesting case I encountered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Incorrect measurement method: repeated measurements without browser restart&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Without browser restart'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runPerformanceTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Execution time changes:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First 10 runs average: 450ms&lt;/li&gt;
&lt;li&gt;Middle 10 runs average: 380ms&lt;/li&gt;
&lt;li&gt;Last 10 runs average: 320ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why does this happen?&lt;/strong&gt;&lt;br&gt;
It's because the V8 engine's JIT compiler learns execution patterns and optimizes the code. In other words, the same code gets faster with repeated execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Correct measurement method: restart browser each time&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'With browser restart'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runPerformanceTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Execution time changes:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First 10 runs average: 445ms&lt;/li&gt;
&lt;li&gt;Middle 10 runs average: 442ms&lt;/li&gt;
&lt;li&gt;Last 10 runs average: 448ms&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Pro Tip&lt;/strong&gt;:&lt;br&gt;
JIT compiler optimizations do occur in production environments.&lt;br&gt;
However, the goal of performance measurement is to obtain a baseline value,&lt;br&gt;
so we should measure in an environment without these optimizations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A physics major turned frontend developer applying experimental methodology...&lt;br&gt;
Who would have thought? 😄 Now, let's dive deeper into analyzing Chrome Trace Reports.&lt;/p&gt;
&lt;h1&gt;
  
  
  Analyzing Chrome Trace Reports
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;The Challenge: Flitter's Performance Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was developing the Flitter visualization framework and felt the need for performance optimization. Flitter uses SVG and Canvas to visualize dynamic data, providing various animations and interactions. While these features enrich data representation, they also cause performance overhead.&lt;/p&gt;

&lt;p&gt;For example, when data changes, &lt;code&gt;requestFrameAnimation&lt;/code&gt; is repeatedly called to redraw SVG elements. Although we optimized the callback function to trigger only once, we didn't realize that calling &lt;code&gt;requestFrameAnimation&lt;/code&gt; itself has a cost. This wasn't noticeable in normal cases but caused performance degradation when rendering complex UI repeatedly (e.g., mouse dragging).&lt;/p&gt;

&lt;p&gt;To solve this issue, we initially tried to track the execution time of specific functions and optimize animation routines. However, we couldn't confidently prove the effectiveness of performance improvements based on subjective feelings.&lt;/p&gt;

&lt;p&gt;"We need a systematic way to record JavaScript execution time and scientifically verify optimization results!"&lt;/p&gt;

&lt;p&gt;This conclusion led us to adopt automated performance measurement using Chrome Trace and Playwright.&lt;/p&gt;
&lt;h2&gt;
  
  
  Collecting Performance Data with Chrome Trace Reports
&lt;/h2&gt;

&lt;p&gt;Chrome Trace Reports record all major events that occur while the browser executes JavaScript code, allowing us to see how much CPU and memory each event consumes. This is particularly useful for analyzing JavaScript function calls and execution times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Use Chrome Trace Reports?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest advantage of Chrome Trace Reports is being able to see the entire function call stack and execution time of each function at a glance. This allows quick identification of specific functions or repetitive code that burden performance. Especially in visualization frameworks like Flitter that interact with multiple elements, finding specific routines that cause performance load is crucial, and Chrome Trace is very helpful in solving these issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automating Chrome Trace with Playwright&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Playwright is a useful tool for automating performance measurements in browser environments and can collect performance data in conjunction with Chrome Trace Reports. With Playwright, you can easily save Trace Reports that track events occurring in the browser. The example code below shows how to record performance data from a specific point and save it as a JSON file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Capture performance traces and save JSON file when diagram is rendered'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Start Chrome trace recording&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`./performance-history/${formatDate(new Date())}.json`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Navigate to the page to measure performance&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://localhost:4173/performance/diagram'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Mark the start of performance measurement&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Perf:Started'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'button'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'svg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Perf:Ended'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'overall'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Perf:Started'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Perf:Ended'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// End measurement and save results&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Analyzing CpuProfile Events
&lt;/h2&gt;

&lt;p&gt;Although we can generate a JSON-formatted Trace Report using Playwright and Chrome Trace, analyzing this file directly is challenging. The report contains all events that occurred during execution, but it's difficult to intuitively understand each function's execution time or call count. We need to preprocess the data to extract meaningful information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CpuProfile Events and Preprocessing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CpuProfile&lt;/code&gt; events are recorded in the &lt;code&gt;disabled-by-default-v8.cpu_profiler&lt;/code&gt; category and track JavaScript function CPU usage. Each event is recorded with a time interval and includes &lt;code&gt;samples&lt;/code&gt; and &lt;code&gt;timeDeltas&lt;/code&gt; fields, which can be used to infer each function's execution time.&lt;/p&gt;

&lt;p&gt;To calculate each function's total execution time, we need to preprocess the data by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterating through &lt;code&gt;samples&lt;/code&gt; and &lt;code&gt;timeDeltas&lt;/code&gt; to calculate each node's total execution time&lt;/li&gt;
&lt;li&gt;Constructing a node hierarchy based on parent-child relationships and aggregating child node execution times to their parents&lt;/li&gt;
&lt;li&gt;Extracting function-level execution times and visualizing or identifying optimization points&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This preprocessing step provides a clear understanding of each function's resource usage and helps identify performance bottlenecks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the ChromeTraceAnalyzer Class
&lt;/h2&gt;

&lt;p&gt;To automate the analysis of Trace Reports, we'll implement the &lt;code&gt;ChromeTraceAnalyzer&lt;/code&gt; class. This class will take Trace data as input, calculate each function's execution time, and provide performance metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChromeTraceAnalyzer Class&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChromeTraceAnalyzer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the execution time of a specific function (in ms)&lt;/span&gt;
    &lt;span class="nf"&gt;getDurationMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nodes is not initialized'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;functionName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Convert to milliseconds&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Set up Trace data and calculate function execution times&lt;/span&gt;
    &lt;span class="nf"&gt;setConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;traceEvents&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Filter 'ProfileChunk' events&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;profileChunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;traceEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'ProfileChunk'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Extract CpuProfile nodes and sample data&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profileChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpuProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;sampleTimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Aggregate sample execution times&lt;/span&gt;
        &lt;span class="n"&gt;profileChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cpuProfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;samples&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeDeltas&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timeDeltas&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;sampleTimes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampleTimes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Construct node hierarchy and aggregate child node execution times&lt;/span&gt;
        &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;callFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sampleTimes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Establish parent-child relationships and aggregate execution times&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;nodesMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;nodesMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;parentNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nodesMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="n"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repeated Measurements for Reliability
&lt;/h2&gt;

&lt;p&gt;Although we can analyze Trace data using &lt;code&gt;ChromeTraceAnalyzer&lt;/code&gt;, a single measurement is not enough to evaluate performance. JavaScript execution performance can vary due to environmental changes, so we need to repeat measurements multiple times to obtain reliable data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repeated Measurement Code Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Capture analyzed trace when diagram is rendered with multiple runs'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'http://localhost:4173/performance/diagram'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Start tracing&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Perf:Started'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'button'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'svg'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Perf:Ended'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'overall'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Perf:Started'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Perf:Ended'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Extract Trace data and analyze&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopTracing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'utf8'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeTraceAnalyzer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runApp&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDurationMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'runApp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDurationMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mount'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;draw&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDurationMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'draw'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDurationMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'layout'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paint&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDurationMs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'paint'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'**** Average Execution Time ****'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`runApp: ${duration.runApp}ms`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`mount: ${duration.mount}ms`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`draw: ${duration.draw}ms`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`layout: ${duration.layout}ms`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`paint: ${duration.paint}ms`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'********************************'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Visualizing Performance Data
&lt;/h2&gt;

&lt;p&gt;By repeating measurements and analyzing Trace data, we can obtain reliable performance metrics. To better understand performance changes over time, we can visualize the data using Flitter.&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%2Fqvvfzocdnhp2a0i84l00.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%2Fqvvfzocdnhp2a0i84l00.png" alt="flitter-chart" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flitter-Based Stacked Bar Chart&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We created a stacked bar chart using Flitter to display the average execution time of each function over multiple runs. This chart helps us identify performance bottlenecks and track changes in execution time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chart Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;X-axis: Measurement dates (track performance changes over time)&lt;/li&gt;
&lt;li&gt;Y-axis: Average execution time (ms)&lt;/li&gt;
&lt;li&gt;Stacked bars: Each function's execution time (color-coded)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance Changes Over Time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By analyzing the chart, we can see how performance changes over time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Date-based performance changes&lt;/strong&gt;: We can track how performance changes on each measurement date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function-level execution time&lt;/strong&gt;: We can identify which functions consume the most resources and optimize them accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current Flitter library can be used as an efficient visualization solution in situations where performance optimization is important. You can check the related code on GitHub, and if you found this helpful, please give it a Star and thumbs up on GitHub! If you show interest in this project, we plan to continue developing and sharing performance improvements and various features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/meursyphus/flitter/blob/latest/packages/test/tests/tracking-performance.test.ts" rel="noopener noreferrer"&gt;https://github.com/meursyphus/flitter/blob/latest/packages/test/tests/tracking-performance.test.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://flitter.dev" rel="noopener noreferrer"&gt;Flitter Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Portfolio of a 4-Year Frontend Developer</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Tue, 10 Dec 2024 12:19:07 +0000</pubDate>
      <link>https://dev.to/moondaeseung/portfolio-of-a-4-year-frontend-developer-5g01</link>
      <guid>https://dev.to/moondaeseung/portfolio-of-a-4-year-frontend-developer-5g01</guid>
      <description>&lt;p&gt;Hello! In this post, I'd like to introduce the frontend projects I've been working on. Instead of just showing my work, I'll focus on the thought process behind the interactions and UX choices, and how I solved the challenges I faced.&lt;/p&gt;

&lt;p&gt;Ultimately, I'll be sharing the choices I made to improve the user experience and the insights I've gained from real-world practice. If you've been curious about my work, I think you'll find this interesting. So, shall we get started?&lt;/p&gt;

&lt;h2&gt;
  
  
  Project 1: Improving Search UX – Smooth Page Transitions and Slide Interactions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sbv9tfzoetq12670k4v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sbv9tfzoetq12670k4v.gif" alt=" " width="600" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If page transitions in a search interface aren't smooth, users can easily get confused. Especially when browsing through multiple pages of search results, abrupt page changes can make it difficult to understand where they are or what they’ll see next. To address this, I implemented slide interactions for the search results pages.&lt;/p&gt;

&lt;p&gt;When moving to the next page, the content slides to the left, and when returning to the previous page, it slides to the right. Maintaining a consistent transition speed was key. For instance, moving directly from page 1 to page 3 required a longer slide duration since the distance covered was greater. This helped create smooth, natural transitions between pages.&lt;/p&gt;

&lt;p&gt;Another crucial aspect of UX is maintaining context. Rather than simply refreshing the page, showing the transition visually allows users to better understand where they are and what they'll see next. I also ensured a smooth flow between pages by connecting the last row of the previous page with the first row of the next page.&lt;/p&gt;

&lt;p&gt;Technically, managing the DOM was a significant challenge. Preloading all the content during page transitions would overload both the browser and server, so I handled this by removing unnecessary DOM elements as the animation ended. Additionally, I added a fade-out effect for the disappearing page's content to make the transition feel even more seamless. This effect was a designer's request, and though it was challenging to implement, the visual satisfaction was worth it.&lt;/p&gt;

&lt;p&gt;As a result, page transitions became smooth and natural, allowing users to explore search results without losing context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project 2: Improving Search UX – Analyzing and Applying Naver's Search Interface
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiaod1i5rn8goy8ehw1h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiaod1i5rn8goy8ehw1h.gif" alt="comwit" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I want to talk about search UX. The search bar is one of the key interfaces that provide a search experience, and its usability greatly depends on how quickly and intuitively it delivers search results. So, I analyzed Naver's search UX and applied it to my own search component.&lt;/p&gt;

&lt;p&gt;Naver's search bar shows search results in real time as users type and highlights matching terms with color. This structure helps users quickly find what they're looking for by providing rapid visual feedback while typing. This makes the search results clearer and more intuitive.&lt;/p&gt;

&lt;p&gt;I also paid attention to the triggers for showing and hiding the search results panel. Naver's search bar opens the results panel when the user starts typing or presses the arrow key, and closes it when they clear the input or click outside. These subtle interactions make the user experience smoother. UX that responds to multiple triggers like this helps users stay immersed in the search task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Applied Interactions and UX Improvements
&lt;/h3&gt;

&lt;p&gt;Based on this analysis, I incorporated similar UX elements into my search component. I designed it to display search results in real time while typing, highlighting the parts that match the search term to provide feedback to the user. Additionally, users could navigate the results using keyboard arrow keys, which improves usability by enabling navigation without the mouse.&lt;/p&gt;

&lt;p&gt;I also handled the triggers for automatically opening and closing the search results panel more meticulously. When the user starts typing, the results panel opens automatically, and when the input is cleared, the panel closes. While the search bar is open, users can also use various keyboard actions (e.g., arrow keys, enter key) to select results, allowing them to check the results more quickly and intuitively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project 3: Scroll Animation – Code Typing Effect on Page Scroll
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc35h4gb0rsdmeunf3zov.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc35h4gb0rsdmeunf3zov.gif" alt="asdn" width="600" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I’d like to discuss scroll animation. Scroll animations are a key element in enhancing the user experience by adding visual effects to a webpage, making the experience more engaging. In particular, I wanted to provide a more lively experience for users of the Flitter.dev project through scroll animations.&lt;/p&gt;

&lt;p&gt;Adding scroll animations helps draw users' attention and increases the time they spend on the page. From the user's perspective, it can feel like the page comes alive as they scroll. The scroll animation I applied to Flitter.dev makes it look like code is being typed in real time, giving the impression that the code is actually being written on the screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Method
&lt;/h3&gt;

&lt;p&gt;To implement this animation, I used Lottie animations. The process was as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write the complete code and then undo it line by line: After completing the entire code, I used the undo function to erase lines one by one, making it look like typing.&lt;/li&gt;
&lt;li&gt;Screen recording: I saved this process as a screen recording and converted it to MP4 format.&lt;/li&gt;
&lt;li&gt;Lottie conversion: I then converted the MP4 video to a Lottie file, allowing the typing animation to progress as the user scrolls.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thanks to this approach, I was able to implement a smooth, natural animation without affecting the page loading speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  UX Impact
&lt;/h3&gt;

&lt;p&gt;The biggest advantage of this scroll animation is that it provides intuitive feedback to users. The real-time typing animation keeps users engaged with the content and draws their attention as they scroll through the page.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://flitter.dev" rel="noopener noreferrer"&gt;https://flitter.dev&lt;/a&gt; to check out the animation for yourself! 🌟&lt;/p&gt;

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

&lt;p&gt;I've introduced a few of the projects I've worked on so far. Though they're not perfect, I believe I’m growing step by step through these experiences. I'm constantly realizing how important user experience (UX) is, and I'll keep striving to create better interactions and UI in the future.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and I hope these projects might serve as a small reference for your own work. 😄&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Can't Ask GPT Properly? Congratulations! You're Growing as a Developer</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Tue, 10 Dec 2024 12:16:13 +0000</pubDate>
      <link>https://dev.to/moondaeseung/cant-ask-gpt-properly-congratulations-youre-growing-as-a-developer-4h7e</link>
      <guid>https://dev.to/moondaeseung/cant-ask-gpt-properly-congratulations-youre-growing-as-a-developer-4h7e</guid>
      <description>&lt;p&gt;"I asked GPT but the answer was weird..."&lt;/p&gt;

&lt;p&gt;This is a common message in developer chat rooms. Usually followed by a lengthy error log dump, without context or any explanation of what they tried to do.&lt;/p&gt;

&lt;p&gt;"Sorry, but how do I fix this?"&lt;br&gt;&lt;br&gt;
"This isn't working, and GPT doesn't understand what I'm saying 😢"&lt;/p&gt;

&lt;p&gt;Even senior developers from prestigious companies like Toss, Musinsa, and Naver can't understand these questions. But this isn't their fault. The questions are simply too vague.&lt;/p&gt;

&lt;h2&gt;
  
  
  GPT Doesn't Understand You? Congratulations!
&lt;/h2&gt;

&lt;p&gt;Sound strange? But I mean it. If GPT can't understand your questions, it's evidence that you're experiencing growing pains.&lt;/p&gt;

&lt;p&gt;Think about it. How can AI understand questions that even senior developers can't comprehend? While GPT is certainly a powerful tool, it's not a magic wand. Without clear context and accurate information, even the world's smartest AI can't provide a proper answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Difficulty with GPT Usage is a Sign of Growth
&lt;/h2&gt;

&lt;p&gt;Junior developer A had this concern:&lt;br&gt;
"Infinite scroll isn't working in React. I asked GPT but the answer was weird."&lt;/p&gt;

&lt;p&gt;Six months later, A asks like this:&lt;br&gt;
"I'm implementing infinite scroll with React Query, using Intersection Observer. I want to store the scroll position in sessionStorage to restore it on back navigation, but I'm seeing unnecessary re-renders. Should I apply memoization?"&lt;/p&gt;

&lt;p&gt;See the difference?&lt;/p&gt;

&lt;p&gt;Paradoxically, finding it difficult to use GPT is a signal that you're growing. As you understand technology more deeply, your questions become more specific and clear. And that's when GPT truly becomes your pair programmer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites for Clear Questions
&lt;/h2&gt;

&lt;p&gt;Here's what you need to properly utilize GPT:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Accurate understanding of the problem situation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What you're trying to achieve&lt;/li&gt;
&lt;li&gt;Current state&lt;/li&gt;
&lt;li&gt;What errors are occurring&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Basic knowledge of your technology stack&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Role of each library or framework&lt;/li&gt;
&lt;li&gt;Core concepts and how they work&lt;/li&gt;
&lt;li&gt;Common problem-solving patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Solutions you've attempted&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What approaches you've tried&lt;/li&gt;
&lt;li&gt;Results from each attempt&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these preparations, GPT can provide much more useful answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Growing Developers Use GPT
&lt;/h2&gt;

&lt;p&gt;Stop asking GPT questions. Instead, give it commands.&lt;/p&gt;

&lt;p&gt;Not "What should I do about this error?"&lt;br&gt;&lt;br&gt;
But "Here's what I tried. Looking at the logs, the problem seems to be here. Suggest how I should modify this part."&lt;/p&gt;

&lt;p&gt;Not "How do I build this feature?"&lt;br&gt;&lt;br&gt;
But "Here are the requirements, and this is my tech stack. Analyze the best implementation approach considering performance and maintainability."&lt;/p&gt;

&lt;p&gt;This approach leads to remarkable changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deeper understanding of problems&lt;/li&gt;
&lt;li&gt;Better control over solutions&lt;/li&gt;
&lt;li&gt;Improved technical specification writing skills&lt;/li&gt;
&lt;li&gt;Enhanced communication with colleagues&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  This is Just the Beginning
&lt;/h2&gt;

&lt;p&gt;Still finding it difficult to ask GPT questions? Don't worry. That's evidence you're growing.&lt;/p&gt;

&lt;p&gt;As questions become more challenging, you learn more, and as you learn more, questions become clearer. And through this process, you'll find yourself growing from a junior to a mid-level developer.&lt;/p&gt;

&lt;p&gt;Start today. Stop asking GPT questions, and start giving it commands. Your real growth begins now.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Think 'use client' Means CSR? Master This Next.js Interview Question</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Tue, 10 Dec 2024 12:15:45 +0000</pubDate>
      <link>https://dev.to/moondaeseung/think-use-client-means-csr-master-this-nextjs-interview-question-297n</link>
      <guid>https://dev.to/moondaeseung/think-use-client-means-csr-master-this-nextjs-interview-question-297n</guid>
      <description>&lt;p&gt;If you're a junior developer who has worked with Next.js, you've probably encountered this question in technical interviews:&lt;br&gt;&lt;br&gt;
"Is &lt;code&gt;use client&lt;/code&gt; exclusively for Client-Side Rendering (CSR)?"&lt;br&gt;&lt;br&gt;
If you can confidently answer "No, components with &lt;code&gt;use client&lt;/code&gt; can also utilize SSR (Server-Side Rendering)!", you'll leave a strong impression on your interviewer.&lt;/p&gt;

&lt;p&gt;In this article, we'll focus on SSR and Server Components to clearly understand what &lt;code&gt;use client&lt;/code&gt; really means and what the Hydration process entails. We'll also explore how all React components were client components before Server Components came along, and examine the performance benefits of Server Components to help you prepare thoroughly for Next.js interviews.&lt;/p&gt;


&lt;h2&gt;
  
  
  Understanding SSR, CSR, and Hydration
&lt;/h2&gt;

&lt;p&gt;Traditionally, React applications often operated with CSR (Client-Side Rendering). The browser receives basic HTML and JS bundles from the server, but the initial screen only contains an empty &lt;code&gt;&amp;lt;div id="root"&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;, leaving users staring at a blank screen until JS loads and rendering completes. This delays initial loading times and negatively impacts SEO.&lt;/p&gt;

&lt;p&gt;One solution to this problem is SSR (Server-Side Rendering). With SSR, React components are rendered to HTML on the server before being sent to the client, allowing users to see actual content instead of a blank screen during initial load. However, this HTML is still in a "dry (Markup-only)" state without interactivity, and only becomes fully interactive after the browser loads JavaScript and React completes its hydration process by registering event handlers.&lt;/p&gt;
&lt;h3&gt;
  
  
  What is Hydration?
&lt;/h3&gt;

&lt;p&gt;Hydration is the process where static HTML rendered on the server is matched with React's virtual DOM on the client side, and event handlers are attached to create an interactive UI. In other words, after receiving "structure-only" HTML through SSR, once JavaScript loads and hydrate() executes, event and state management logic becomes active on top of this HTML structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Server: SSR] --(HTML)--&amp;gt; [Browser: Initial Display] 
                                      | 
                                      v 
                          (JS Load, hydrate()) 
                                      |
                                      v
                    [Browser: Event Handlers Attached, VDOM Sync]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now users can see content immediately upon page load, with interactivity becoming available as soon as JS is ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  Distinguishing Server Components from Client Components
&lt;/h2&gt;

&lt;p&gt;Server Components, introduced after React 18, presented a new paradigm of "components that only run on the server." In Next.js, all components are Server Components by default, and only components that need to run on the client explicitly declare &lt;code&gt;use client&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Server Components are delivered to the client as HTML snippets and are excluded from hydration. This means the HTML returned by Server Components is simply inserted into the browser "as is," without event handlers or state being injected on the client side. As a result, Server Components consume minimal JavaScript bundle size, significantly helping with performance optimization.&lt;/p&gt;

&lt;p&gt;On the other hand, components declaring &lt;code&gt;use client&lt;/code&gt; behave like traditional React components. They generate initial HTML through SSR, then go through the hydration process to activate interactivity. Therefore, &lt;code&gt;use client&lt;/code&gt; components can utilize SSR and support the hydration process that combines SSR + CSR.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before Server Components: All Components Were Client Components
&lt;/h2&gt;

&lt;p&gt;Before Server Components existed, all React components had to go through hydration on the client side. Even with SSR, all components ultimately needed to have their logic re-injected with JavaScript on the client. This led to large bundle sizes and longer initial loading times in large applications.&lt;/p&gt;

&lt;p&gt;After the introduction of Server Components, we can now handle "static parts that only need to be displayed" as Server Components without any declaration, while parts requiring interactivity can be marked with &lt;code&gt;use client&lt;/code&gt;. This clear separation enables more efficient bundle optimization and better performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Server Components Work: Slot Insertion and HTML Combination
&lt;/h2&gt;

&lt;p&gt;Server Components render HTML snippets on the server and provide them to the client, while React's render tree leaves a "hole" for these snippets. The final HTML is created by inserting these snippets into the corresponding holes. Here's a simple ASCII diagram illustrating this process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Server: RSC(Render)] --&amp;gt; [HTML snippet] 
                                | 
                                v 
                [React Render Tree]: "hole" creation
                                | 
                                v 
                    Combined HTML --&amp;gt; Client transmission 
                    (Server Components excluded from hydration)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this process, Server Components do not include JavaScript bundles. As a result, they cannot register event handlers or state, but this is actually beneficial for parts that only require static HTML. The reduced JavaScript bundle size leads to faster initial screen display.&lt;/p&gt;




&lt;h2&gt;
  
  
  Acing Your Interview: Confidently Answering "Is 'use client' Only for CSR?"
&lt;/h2&gt;

&lt;p&gt;For example, you might encounter this question in an interview:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interviewer:&lt;/strong&gt; "Do components with &lt;code&gt;use client&lt;/code&gt; only support CSR?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model Answer:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
"No, in Next.js, all components are Server Components by default, and only components that need to run on the client explicitly declare &lt;code&gt;use client&lt;/code&gt;. These client components generate initial HTML through SSR and then go through the hydration process to activate interactivity on the client side. Therefore, &lt;code&gt;use client&lt;/code&gt; components can also utilize SSR. On the other hand, Server Components are excluded from hydration and only provide static HTML, reducing JavaScript bundle size and improving performance."&lt;/p&gt;

&lt;p&gt;If you can clearly explain this, the interviewer will recognize that you have a solid grasp of Server Components, SSR, and hydration concepts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Server-Side Rendering and Server Components Are Not Mutually Exclusive
&lt;/h2&gt;

&lt;p&gt;Finally, it's essential to note that Server Components do not replace SSR. SSR focuses on displaying content to users immediately during initial load, while Server Components provide an additional layer for optimizing bundle size and data fetching. These two technologies complement each other, and using them together enables you to create a better user experience and improve performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: What Is 'use server'?
&lt;/h2&gt;

&lt;p&gt;'use server' is a different concept from Server Components, used to define Server Actions. Server Actions allow clients to directly call server functions, making them useful for handling server-side tasks like form submissions or data updates.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All Next.js components are Server Components by default&lt;/li&gt;
&lt;li&gt;Client components require explicit &lt;code&gt;use client&lt;/code&gt; declaration&lt;/li&gt;
&lt;li&gt;Client components can utilize SSR: server-side HTML rendering followed by hydration&lt;/li&gt;
&lt;li&gt;Server Components provide static HTML without hydration, reducing JavaScript bundle size and improving performance&lt;/li&gt;
&lt;li&gt;'use server' is a separate feature for defining Server Actions&lt;/li&gt;
&lt;li&gt;Server Components complement SSR, not replace it: together, they provide optimal user experience and performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Armed with this knowledge, confidently answer interview questions. Emphasize that "use client components also support SSR" and explain how Server Components lead to bundle optimization and performance improvements. Highlighting that all components are Server Components by default and only client components need the 'use client' directive will demonstrate your deep understanding of Next.js architecture.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>If you're a front-end developer, you should at least know how to handle page transitions, right? Can't do it? 😱</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Wed, 09 Oct 2024 07:53:51 +0000</pubDate>
      <link>https://dev.to/moondaeseung/if-youre-a-front-end-developer-you-should-at-least-know-how-to-handle-page-transitions-right-cant-do-it-51h4</link>
      <guid>https://dev.to/moondaeseung/if-youre-a-front-end-developer-you-should-at-least-know-how-to-handle-page-transitions-right-cant-do-it-51h4</guid>
      <description>&lt;p&gt;When we use smartphone apps, smooth screen transitions are an expected part of the experience. But on the web, it’s a different story. If developers don’t pay attention to page transitions, websites can feel jarring, with sudden screen changes every time you click, leaving users confused or, worse, making them leave the site.&lt;/p&gt;

&lt;p&gt;An experience where the screen suddenly switches, like “teleportation,” places a significant cognitive load on users. Ultimately, good UX should minimize this burden and create a natural flow. So, why are so many websites still overlooking this crucial aspect?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now, mastering this will give you a competitive edge!&lt;/strong&gt; By learning page transition techniques, you’ll not only enhance your web development skills but also gain an edge in app development. Thanks to technologies like &lt;strong&gt;WebView&lt;/strong&gt;, web front-end developers can easily start developing apps. If you can handle page transitions on the web, you can pretty much call yourself an app developer, right? 😎&lt;/p&gt;

&lt;h2&gt;
  
  
  Is your website still outdated?
&lt;/h2&gt;

&lt;p&gt;As a web developer, I’ve always been dissatisfied with how page transitions are handled on websites. Especially when transitioning between pages, the abrupt change of screen often makes users lose track of where they are or what they just clicked. I’ve always thought that such sudden shifts severely degrade the user experience. While smooth transitions are standard in apps, the web often still feels like “teleportation” when changing pages.&lt;/p&gt;

&lt;p&gt;To solve this, I initially tried simple CSS tricks to add smoother transitions. However, I quickly realized their limitations. While the transitions themselves became smoother, the issue of completely new screens appearing with every page load remained. I thought that to reduce users’ cognitive fatigue on the web, we needed transitions that not only flowed smoothly but also preserved the overall context.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, I present to you, SSGOI!!
&lt;/h2&gt;

&lt;p&gt;As I became more frustrated with page transitions, I naturally began thinking about solutions. Abrupt transitions are too common on the web. When the screen changes suddenly, users often find it hard to orient themselves, leading to a loss of focus. This experience made me realize the need for smoother, more intuitive transitions on the web as well.&lt;/p&gt;

&lt;p&gt;That’s when I decided to create &lt;strong&gt;SSGOI&lt;/strong&gt;, a library designed to provide seamless, app-like page transitions for the web. &lt;strong&gt;SSGOI&lt;/strong&gt; offers various animation effects and ensures users can clearly understand where they’re navigating.&lt;/p&gt;

&lt;p&gt;The biggest advantages of this library are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Various transition effects&lt;/strong&gt;: With several built-in animation options, developers can easily implement smooth page transitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable transitions&lt;/strong&gt;: Dynamic transitions based on runtime conditions allow for a tailored user experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser compatibility&lt;/strong&gt;: Designed to work consistently across different browsers, SSGOI leverages the latest browser APIs while still supporting smooth transitions in browsers with limitations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cw38jyij2wrm7xdir8y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cw38jyij2wrm7xdir8y.gif" alt="page transtion" width="719" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to know the secret to smooth transitions like Pinterest?
&lt;/h2&gt;

&lt;p&gt;Implementing Pinterest-like transitions with &lt;strong&gt;SSGOI&lt;/strong&gt; wasn’t just about visual delight; it was about offering a more intuitive user experience. In image-centric layouts like Pinterest, it’s crucial that when a user clicks on an image, the context is maintained while the screen transitions smoothly.&lt;/p&gt;

&lt;p&gt;My goal was to have the clicked image smoothly &lt;strong&gt;enlarge&lt;/strong&gt; to the center of the screen, while the rest of the elements naturally fade away. This ensures that users stay focused on their selected content, and the page transitions without the common “disconnect” often felt on the web. It was also vital to ensure that during the image’s resizing, &lt;strong&gt;context is maintained&lt;/strong&gt;, so users clearly understand where they’re navigating.&lt;/p&gt;

&lt;p&gt;However, implementing this transition was no easy task. I had to carefully calculate how the image’s size and position would change across various screen sizes during the transition. Especially when the URL actually changes between pages, there was a lot of mathematical work involved in maintaining a smooth transition.&lt;/p&gt;

&lt;p&gt;In the end, I was able to overcome the complex calculations and deliver a seamless experience, as if using an app right on the web.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3r4oc4c6opuxc4psjrc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3r4oc4c6opuxc4psjrc.gif" alt="pinterest in ssgoi" width="516" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My vision for the future.
&lt;/h2&gt;

&lt;p&gt;There are clear limitations in today’s web UX, especially with &lt;strong&gt;browser API restrictions&lt;/strong&gt; and &lt;strong&gt;cross-browser compatibility&lt;/strong&gt;. While smooth, intuitive page transitions are expected in apps, on the web, we still experience sudden screen changes and loss of context. This is because web browsers don’t yet provide all the necessary APIs to handle transitions smoothly. This makes it difficult for developers to easily implement a variety of page transition effects, and cross-browser compatibility adds to the challenge.&lt;/p&gt;

&lt;p&gt;This is where libraries like &lt;strong&gt;SSGOI&lt;/strong&gt; come in. &lt;strong&gt;SSGOI&lt;/strong&gt; bridges the gap, helping to provide an app-like, smooth user experience even on the web. By maintaining &lt;strong&gt;context&lt;/strong&gt; and handling transitions smoothly, it reduces the &lt;strong&gt;cognitive load&lt;/strong&gt; on users during page navigation. This allows developers to offer a better user experience without writing complex code.&lt;/p&gt;

&lt;p&gt;The future of web UX will only improve. With new &lt;strong&gt;browser APIs&lt;/strong&gt; and standards emerging, implementing page transitions will become much easier. Furthermore, as technologies like &lt;strong&gt;AI&lt;/strong&gt; become more integrated into the development process, complex tasks will be handled by AI, allowing developers to focus more on the creative aspects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to implement smooth transitions on the web? Check out SSGOI!
&lt;/h2&gt;

&lt;p&gt;Creating smooth, app-like transitions on the web is now a crucial task. Since user experience has a significant impact on the success of a website, it’s important not to overlook even small details like page transitions.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;SSGOI&lt;/strong&gt; library was designed to address these issues. With this library, you can implement smooth and natural page transitions without writing complex code. Built with cross-browser compatibility in mind, it ensures a consistent user experience across various environments.&lt;/p&gt;

&lt;p&gt;If you’re interested, visit the &lt;a href="https://github.com/meursyphus/ssgoi" rel="noopener noreferrer"&gt;SSGOI GitHub repository&lt;/a&gt;! 😄 If you find it useful, don’t forget to leave a &lt;strong&gt;Star&lt;/strong&gt;! ⭐ Your feedback and participation will help improve web UX for everyone.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>12 Best ER Diagram Tools to Use in 2024</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Wed, 17 Jul 2024 15:05:37 +0000</pubDate>
      <link>https://dev.to/moondaeseung/12-best-er-diagram-tools-to-use-in-2024-8pl</link>
      <guid>https://dev.to/moondaeseung/12-best-er-diagram-tools-to-use-in-2024-8pl</guid>
      <description>&lt;p&gt;ER (Entity-Relationship) diagrams are essential tools for database design. In this article, we'll introduce the best ER diagram tools available in 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;a href="https://www.lucidchart.com/pages/" rel="noopener noreferrer"&gt;Lucidchart&lt;/a&gt;
&lt;/h2&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%2Fzsh6a25rye26x6t27005.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%2Fzsh6a25rye26x6t27005.png" alt="Lucidchart" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lucidchart allows you to create various types of diagrams, including ER diagrams. It can be integrated with MS Office and G Suite.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time collaboration&lt;/li&gt;
&lt;li&gt;Automated diagramming&lt;/li&gt;
&lt;li&gt;Enhanced security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Starting from $7.95/month for a single user account&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;a href="https://www.datanamic.com/" rel="noopener noreferrer"&gt;DeZign for Databases&lt;/a&gt;
&lt;/h2&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%2Fhmxpyshfnv7uw4gxlc3s.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%2Fhmxpyshfnv7uw4gxlc3s.png" alt="DeZign for Databases" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DeZign for Databases is a tool that helps visualize database structures. It can create new databases as well as reverse engineer existing ones.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop interface&lt;/li&gt;
&lt;li&gt;Various display modes&lt;/li&gt;
&lt;li&gt;Industry-standard design notation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: €19/month for Standard Edition, €59/month for Professional Version&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;a href="https://www.gleek.io/" rel="noopener noreferrer"&gt;Gleek.io&lt;/a&gt;
&lt;/h2&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%2Fygjwaelzfjs7830ce10v.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%2Fygjwaelzfjs7830ce10v.png" alt="Gleek.io" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gleek.io is a fast diagramming and flowchart application that works through keyboard commands and code.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard-based user interface&lt;/li&gt;
&lt;li&gt;Fast diagramming&lt;/li&gt;
&lt;li&gt;Version control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Basic app is free, Premium version is €9.95/month&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;a href="https://easyrd.dev" rel="noopener noreferrer"&gt;EasyRd&lt;/a&gt;
&lt;/h2&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%2Fcfana6z9wvtggudyblvy.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%2Fcfana6z9wvtggudyblvy.png" alt=" " width="798" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;EasyRd is a recently emerged innovative ER diagram tool. It operates based on code and utilizes DBML (Database Markup Language), a well-known open-source syntax.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy generation through GPT with DBML syntax support&lt;/li&gt;
&lt;li&gt;Code-based diagram creation&lt;/li&gt;
&lt;li&gt;Completely free to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest advantage of EasyRd is that it can easily generate ER diagrams through AI tools like GPT using DBML syntax. It provides a code-based interface familiar to developers while enabling intuitive visualization. Additionally, it's completely free to use, removing any cost burden.&lt;/p&gt;

&lt;p&gt;This tool will be particularly attractive to developers interested in rapid prototyping or AI-based database design.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;a href="https://www.dbdesigner.net/" rel="noopener noreferrer"&gt;DbDesigner&lt;/a&gt;
&lt;/h2&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%2Frlevno6b3k2v73hr7z2t.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%2Frlevno6b3k2v73hr7z2t.png" alt="DbDesigner" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DbDesigner is suitable for teams that need to create different user classes for projects.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced export options&lt;/li&gt;
&lt;li&gt;Project and team management&lt;/li&gt;
&lt;li&gt;Export and import to SQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: $6/month for basic package, $15/month for unlimited package&lt;/p&gt;

&lt;h2&gt;
  
  
  6. &lt;a href="https://www.smartdraw.com/" rel="noopener noreferrer"&gt;SmartDraw&lt;/a&gt;
&lt;/h2&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%2F86631mqp6zxblbj7hfot.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%2F86631mqp6zxblbj7hfot.png" alt="SmartDraw" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SmartDraw is a web-based tool for diagramming and schema creation.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intelligent formatting&lt;/li&gt;
&lt;li&gt;Professional results&lt;/li&gt;
&lt;li&gt;Visio import and export&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Starting from $9.95/month for a single user account&lt;/p&gt;

&lt;h2&gt;
  
  
  7. &lt;a href="https://www.visual-paradigm.com/" rel="noopener noreferrer"&gt;Visual Paradigm&lt;/a&gt;
&lt;/h2&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%2Fy9g1crmiea5znq7arx1p.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%2Fy9g1crmiea5znq7arx1p.png" alt="Visual Paradigm" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Paradigm comes with several ERD templates and can create various types of diagrams.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UML diagrams&lt;/li&gt;
&lt;li&gt;ER diagrams&lt;/li&gt;
&lt;li&gt;BPMN diagrams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Starting from $6/month, perpetual licenses start at $99 for a single user&lt;/p&gt;

&lt;h2&gt;
  
  
  8. &lt;a href="https://dbdiagram.io/home" rel="noopener noreferrer"&gt;dbdiagram.io&lt;/a&gt;
&lt;/h2&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%2Fwh959l133e1ay6fofgzk.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%2Fwh959l133e1ay6fofgzk.png" alt="dbdiagram.io" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;dbdiagram.io allows power users to easily create ER diagrams.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL statement generation&lt;/li&gt;
&lt;li&gt;Export to image and PDF&lt;/li&gt;
&lt;li&gt;One-click sharing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Basic plan is free, Pro version is $9/month&lt;/p&gt;

&lt;h2&gt;
  
  
  9. &lt;a href="https://www.devart.com/dbforge/sql/studio/" rel="noopener noreferrer"&gt;dBForge Studio for SQL Server&lt;/a&gt;
&lt;/h2&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%2Fia6fqrphy2pcjmdk8jmf.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%2Fia6fqrphy2pcjmdk8jmf.png" alt="dBForge Studio for SQL Server" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;dBForge Studio for SQL Server is an SQL management tool that can also create ER diagrams.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL coding support&lt;/li&gt;
&lt;li&gt;Index manager&lt;/li&gt;
&lt;li&gt;Schema comparison&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Starting from $249.95&lt;/p&gt;

&lt;h2&gt;
  
  
  10. &lt;a href="https://www.navicat.com/en/products/navicat-data-modeler" rel="noopener noreferrer"&gt;Navicat Data Modeler&lt;/a&gt;
&lt;/h2&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%2F443nhsm62yyuf1dtis7v.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%2F443nhsm62yyuf1dtis7v.png" alt="Navicat Data Modeler" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navicat Data Modeler creates diagrams for MySQL, Oracle, MariaDB, PostgreSQL, and SQLite.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database objects&lt;/li&gt;
&lt;li&gt;Reverse engineering&lt;/li&gt;
&lt;li&gt;SQL code generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Enterprise version $22.99/month or $229.99/year (per user)&lt;/p&gt;

&lt;h2&gt;
  
  
  11. &lt;a href="https://creately.com/" rel="noopener noreferrer"&gt;Creately&lt;/a&gt;
&lt;/h2&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%2Fq2qc1enlk5k9xkcqm0rf.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%2Fq2qc1enlk5k9xkcqm0rf.png" alt="Creately" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creately claims to be "your visual workspace" and enables remote collaboration.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smart shapes&lt;/li&gt;
&lt;li&gt;Easy drawing shortcuts&lt;/li&gt;
&lt;li&gt;Real-time collaboration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Team version $6/month (per user)&lt;/p&gt;

&lt;h2&gt;
  
  
  12. &lt;a href="https://www.gliffy.com/" rel="noopener noreferrer"&gt;Gliffy&lt;/a&gt;
&lt;/h2&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%2Fommfbeedih0jx0d6xx86.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%2Fommfbeedih0jx0d6xx86.png" alt="Gliffy" width="500" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gliffy is a diagramming tool that can be easily used by non-developers as well.&lt;/p&gt;

&lt;p&gt;Key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Process flowcharts&lt;/li&gt;
&lt;li&gt;Workflow diagrams&lt;/li&gt;
&lt;li&gt;Data flow charts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price: Pro version $7.99/month (per user)&lt;/p&gt;

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

&lt;p&gt;In 2024, there are various ER diagram tools available, allowing you to choose the one that best fits your project requirements. New tools like EasyRd are making the database design process even more efficient through integration with AI. Given that it's provided for free, why not consider EasyRd for your next project?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Flitter vs D3.js: Revolutionizing Data Visualization for the Web</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Sun, 14 Jul 2024 07:33:46 +0000</pubDate>
      <link>https://dev.to/moondaeseung/flitter-vs-d3js-revolutionizing-data-visualization-for-the-web-2f7h</link>
      <guid>https://dev.to/moondaeseung/flitter-vs-d3js-revolutionizing-data-visualization-for-the-web-2f7h</guid>
      <description>&lt;p&gt;In the world of web-based data visualization, D3.js has long been the go-to library for developers. However, Flitter is changing the game, offering a fresh approach that addresses many of the challenges developers face with D3. Let's explore why Flitter is becoming the preferred choice for modern data visualization projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Ease of Use: Simplifying the Complex
&lt;/h2&gt;

&lt;h3&gt;
  
  
  D3.js Approach:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;svg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"svg"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"circle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;112&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"circle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"cy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"cx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flitter Approach:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CustomPaint&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@meursyphus/flitter"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nc"&gt;BubbleChart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomPaint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;painter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flitter Advantage:&lt;/strong&gt; Flitter's declarative approach and widget-based architecture make it significantly easier to create and understand visualizations, especially for developers already familiar with modern UI frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Performance: Handling Large Datasets with Ease
&lt;/h2&gt;

&lt;p&gt;While D3.js can struggle with large datasets due to its direct DOM manipulation, Flitter's efficient rendering pipeline shines with big data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flitter's Optimized Rendering:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'@meursyphus/flitter'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Widget&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'@meursyphus/flitter-react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Widget&lt;/span&gt;
      &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"100vw"&lt;/span&gt;
      &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"100vh"&lt;/span&gt;
      &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="c1"&gt;// your widget here&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="cm"&gt;/**
       * you can choose between "canvas" and "svg". 
       * canvas is faster, while svg is useful for server side rendering
       */&lt;/span&gt;
      &lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"canvas"&lt;/span&gt;
    &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flitter Advantage:&lt;/strong&gt; Flitter's rendering approach allows for smooth handling of thousands of data points, maintaining high frame rates even with dynamic updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Integration with UI: Seamless Component Integration
&lt;/h2&gt;

&lt;p&gt;D3.js often requires additional work to integrate with modern UI frameworks. Flitter, on the other hand, is designed for seamless integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flitter's Unified Approach:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@meursyphus/flitter"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;BarChart&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@meursyphus/flitter-chart"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Widget&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'@meursyphus/flitter-react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Widget&lt;/span&gt;
      &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"100vw"&lt;/span&gt;
      &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"100vh"&lt;/span&gt;
      &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sales Dashboard"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nc"&gt;BarChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* chart properties */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c1"&gt;// Other UI components&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flitter Advantage:&lt;/strong&gt; Create entire applications with a consistent architecture, mixing visualizations and UI components effortlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Responsive Design: Adapt to Any Screen
&lt;/h2&gt;

&lt;p&gt;While D3.js requires manual work for responsiveness, Flitter makes it straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Container&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"@meursyphus/flitter"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Widget&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'@meursyphus/flitter-react'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nc"&gt;YourWidget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="c1"&gt;// your widget implementation here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Widget&lt;/span&gt;
      &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"100%"&lt;/span&gt;
      &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"100%"&lt;/span&gt;
      &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;YourWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// your widget will be centered whenever the screen size changes&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flitter Advantage:&lt;/strong&gt; Built-in responsiveness features make it easy to create visualizations that look great on any device.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Learning Curve: Familiarity for Modern Developers
&lt;/h2&gt;

&lt;p&gt;D3.js has a steep learning curve, especially for developers used to modern framework paradigms. Flitter leverages familiar concepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InteractiveChart&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InteractiveChartState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InteractiveChartState&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;InteractiveChart&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;selectedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;onDataPointSelected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selectedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;onDataPointClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onDataPointSelected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Selected: ${this.selectedData}`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flitter Advantage:&lt;/strong&gt; Developers familiar with modern UI frameworks can quickly become productive with Flitter, leveraging concepts they already know.&lt;/p&gt;



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


**Flitter Advantage:** Create smooth, performant animations with a simpler, more intuitive API.

## Conclusion: Why Choose Flitter Over D3.js?

1. **Easier Learning Curve:** Familiar concepts for modern developers.
2. **Better Performance:** Efficient handling of large datasets.
3. **Seamless UI Integration:** Build entire applications with a consistent architecture.
4. **Built-in Responsiveness:** Easily create adaptive visualizations.
5. **Simplified Animations:** Create complex animations with less code.

While D3.js remains a powerful tool, Flitter represents the future of web-based data visualization. It combines the flexibility and power needed for complex visualizations with the ease of use and integration capabilities that modern developers expect.

Ready to take your data visualization projects to the next level? Choose Flitter and experience the future of web development today.

Visit here: [Flitter](https://flitter.dev) to get started.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
    </item>
    <item>
      <title>The Complete Guide to CSS object-fit Property</title>
      <dc:creator>Meursyphus</dc:creator>
      <pubDate>Sun, 14 Jul 2024 00:05:53 +0000</pubDate>
      <link>https://dev.to/moondaeseung/the-complete-guide-to-css-object-fit-property-3apa</link>
      <guid>https://dev.to/moondaeseung/the-complete-guide-to-css-object-fit-property-3apa</guid>
      <description>&lt;h1&gt;
  
  
  The Complete Guide to CSS object-fit: Key to Handling Images on the Web
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction: Why You Need to Know object-fit
&lt;/h2&gt;

&lt;p&gt;Dealing with images in web development has always been a challenging task. How can we consistently display images of various sizes and ratios? This is where the CSS &lt;code&gt;object-fit&lt;/code&gt; property plays a crucial role.&lt;/p&gt;

&lt;p&gt;Understanding &lt;code&gt;object-fit&lt;/code&gt; means more than just displaying images correctly. Recently, many cloud services that automatically adjust the size of user-uploaded images have been offering &lt;code&gt;object-fit&lt;/code&gt; as an option. Therefore, if you don't understand the exact behavior of &lt;code&gt;object-fit&lt;/code&gt;, you might unintentionally provide a negative user experience during the image optimization process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Concepts of object-fit
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;object-fit&lt;/code&gt; property determines how an image will be cropped or scaled to fit within the specified size (container size) of an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag, in relation to the original image size.&lt;/p&gt;

&lt;p&gt;An important point to note is that when only &lt;code&gt;width&lt;/code&gt; or &lt;code&gt;height&lt;/code&gt; is specified, the other dimension is automatically determined based on the original image ratio. In this case, for all &lt;code&gt;object-fit&lt;/code&gt; values except &lt;code&gt;scale-down&lt;/code&gt; and &lt;code&gt;none&lt;/code&gt;, the image will fill the container size while maintaining its aspect ratio. However, with &lt;code&gt;scale-down&lt;/code&gt; and &lt;code&gt;none&lt;/code&gt;, if the container size is larger than the image size, the image will maintain its original size without stretching.&lt;/p&gt;

&lt;h2&gt;
  
  
  object-fit Values and Their Effects
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. contain
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      Container
+--------------------+
|        Image       |
|:------------------:|
|:                  :|
|:                  :|
|:                  :|
|:                  :|
|:------------------:|
|                    |
+--------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Adjusts the image to be fully visible&lt;/li&gt;
&lt;li&gt;Maintains image aspect ratio&lt;/li&gt;
&lt;li&gt;May result in empty space within the container&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. cover
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         Image
:######################:
:#                    #:
:#     Container      #:
:#  +------------+    #:
:#  |            |    #:
:#  |            |    #:
:#  |            |    #:
:#  +------------+    #:
:#                    #:
:######################:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Completely fills the container&lt;/li&gt;
&lt;li&gt;Maintains image aspect ratio&lt;/li&gt;
&lt;li&gt;May crop parts of the image&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. fill
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Completely fills the container&lt;/li&gt;
&lt;li&gt;Ignores image aspect ratio (may distort the image)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. none
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Maintains original image size&lt;/li&gt;
&lt;li&gt;Ignores container size (image may be clipped)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. scale-down
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Displays the image at the smaller size between &lt;code&gt;none&lt;/code&gt; and &lt;code&gt;contain&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prevents the image from stretching&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Widget in Flitter
&lt;/h2&gt;

&lt;p&gt;The Flitter library also provides an Image widget that implements the behavior of &lt;code&gt;object-fit&lt;/code&gt;. Flitter's Image widget is designed to behave as similarly as possible to the native HTML &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag and supports various &lt;code&gt;object-fit&lt;/code&gt; options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'https://flitter.dev/examples/object-fit/profile.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;objectFit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'none'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see various examples of &lt;code&gt;object-fit&lt;/code&gt;, visit the following URL: &lt;a href="https://flitter.dev/examples/object-fit" rel="noopener noreferrer"&gt;https://flitter.dev/examples/object-fit&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The &lt;code&gt;object-fit&lt;/code&gt; property is a powerful tool for handling images on the web. By properly understanding and using it, developers can effectively manage images of various sizes and ratios, providing users with a consistent visual experience. Understanding &lt;code&gt;object-fit&lt;/code&gt; becomes even more important when using image optimization services. We hope this guide helps you handle images more effectively in your web projects.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
