<?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: Ansh Saini</title>
    <description>The latest articles on DEV Community by Ansh Saini (@anshsaini).</description>
    <link>https://dev.to/anshsaini</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%2F230500%2F457006ba-6d72-441c-974b-cdc1374cc271.png</url>
      <title>DEV Community: Ansh Saini</title>
      <link>https://dev.to/anshsaini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anshsaini"/>
    <language>en</language>
    <item>
      <title>Grow a Song: my run wrote its own soundtrack, with Gemma running on my phone</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sat, 10 Oct 2026 08:38:35 +0000</pubDate>
      <link>https://dev.to/anshsaini/grow-a-song-my-run-wrote-its-own-soundtrack-with-gemma-running-on-my-phone-44ml</link>
      <guid>https://dev.to/anshsaini/grow-a-song-my-run-wrote-its-own-soundtrack-with-gemma-running-on-my-phone-44ml</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge, Week 1: Touch Grass&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;On Friday morning I went for a 35 minute run. When I started, my song was a single low drone. By the time I got back it had pads, a bass line, drums, a kalimba melody, and ten seconds of the actual street I ran down mixed in underneath.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grow a Song&lt;/strong&gt; turns time outside into music. Every minute you spend outdoors in daylight adds a layer to today's track:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Outside time (20 min target)&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0 min&lt;/td&gt;
&lt;td&gt;drone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 min&lt;/td&gt;
&lt;td&gt;pads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;td&gt;bass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 min&lt;/td&gt;
&lt;td&gt;drums&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13 min&lt;/td&gt;
&lt;td&gt;melody&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 min&lt;/td&gt;
&lt;td&gt;a field recording of where you are&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you stay inside all day, your song stays thin, and it only fills in once you get out. There's no way to skip ahead or buy layers, so the only way to hear the whole song is to go outside.&lt;/p&gt;

&lt;p&gt;The composer is &lt;strong&gt;Gemma 3 1B, running on my iPhone 13 Pro&lt;/strong&gt;. Before each walk or run it looks at the time of day, how high the sun is, what you're about to do, and how much you've been outside this week, then picks the key, scale, tempo, chord progression, instruments, a title and a one-line liner note. My run got this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ephemeral Echoes&lt;/strong&gt;. D dorian, 160 bpm, chords 1 7 5 3, warm pads, pulse bass, driving drums, kalimba.&lt;br&gt;
&lt;em&gt;"The world stretches out beneath you, a slow, expectant invitation."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There's no account or server behind it, and after a one-time model download it works in airplane mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/LCFFlzZQWFE" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here is the song from my run, rendered by the same engine the app uses, plus a version that replays the run with the layers arriving one at a time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://res.cloudinary.com/dz05pa4ud/video/upload/v1791619582/2026-10-09_Ephemeral_Echoes_fvm7ke.m4a" rel="noopener noreferrer"&gt;Ephemeral Echoes (full song)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://res.cloudinary.com/dz05pa4ud/video/upload/v1791619583/2026-10-09_Ephemeral_Echoes_growth_ix2hzm.m4a" rel="noopener noreferrer"&gt;Ephemeral Echoes (how it grew, layer by layer)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The numbers from that morning, pulled off the phone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;35.1 minutes counted as outside, 3.4 km&lt;/li&gt;
&lt;li&gt;all 6 layers playing by the end (my target was 20 minutes)&lt;/li&gt;
&lt;li&gt;a 10 second clip recorded at 7:40 AM, now the top layer of the song&lt;/li&gt;
&lt;li&gt;Gemma's field note afterwards: &lt;em&gt;"You ran 3.2 km. 'Ephemeral Echoes' is now a part of the recording."&lt;/em&gt; (Accurate in spirit. The distance is off by about 200 m, which is what you get from a 1B model writing journal entries.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/grow-a-song" rel="noopener noreferrer"&gt;
        grow-a-song
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/grow-a-song/App/Assets.xcassets/AppIcon.appiconset/icon.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fgrow-a-song%2FHEAD%2FApp%2FAssets.xcassets%2FAppIcon.appiconset%2Ficon.png" width="140" alt="Grow a Song icon"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Grow a Song&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;
  Your time outside becomes music.&lt;br&gt;
  Every minute outdoors adds a layer to today's song, composed on your iPhone by an open-weight model
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://youtube.com/shorts/LCFFlzZQWFE" rel="nofollow noopener noreferrer"&gt;Demo video&lt;/a&gt; ·
  &lt;a href="https://res.cloudinary.com/dz05pa4ud/video/upload/v1791619582/2026-10-09_Ephemeral_Echoes_fvm7ke.m4a" rel="nofollow noopener noreferrer"&gt;Listen to a real run&lt;/a&gt; ·
  &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05" rel="nofollow"&gt;DEV challenge&lt;/a&gt;
&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Start a walk or a run. Your song begins as a single low drone. As you spend time outside,
new layers fade in:&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Share of your daily target&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0 %&lt;/td&gt;
&lt;td&gt;Drone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 %&lt;/td&gt;
&lt;td&gt;Pads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25 %&lt;/td&gt;
&lt;td&gt;Bass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42 %&lt;/td&gt;
&lt;td&gt;Drums&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;67 %&lt;/td&gt;
&lt;td&gt;Melody&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 %&lt;/td&gt;
&lt;td&gt;A 10 second field recording of where you are&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Stay inside and your song stays thin. The only way to hear all of it is to go outside.&lt;/p&gt;
&lt;p&gt;Before each outing, &lt;strong&gt;Gemma 3 1B runs on the phone&lt;/strong&gt; and composes the song: key, scale
tempo, chord progression, instruments, a title and a one-line liner note, based on the
time of day, the sun's height, what…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/grow-a-song" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Swift, SwiftUI, AVAudioEngine, CoreLocation, CoreMotion, HealthKit and &lt;a href="https://github.com/ml-explore/mlx-swift-lm" rel="noopener noreferrer"&gt;MLX Swift LM&lt;/a&gt;. The music engine is a plain Swift package with 37 tests, so most of it runs and tests on a Mac without a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gemma composes, Swift plays
&lt;/h3&gt;

&lt;p&gt;I didn't want a 1B model generating audio or MIDI note by note. A model that small does fine at picking a mood and badly at keeping track of hundreds of notes, so Gemma makes about ten musical decisions and returns them as JSON:&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ephemeral Echoes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"scale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dorian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"bpm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"progression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pad"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"bass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pulse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"drums"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"driving"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"melody"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kalimba"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"liner_note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The world stretches out beneath you, a slow, expectant invitation."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A deterministic sequencer turns that into 16 bars of notes (melody in an A A' B A shape so it repeats enough to stick), and a small synth written from scratch plays it: detuned saw pads, an FM kalimba, a kick that's just a sine wave sweeping down from 155 Hz, and a reverb. The same spec always produces the same song, which turned out to matter later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting reliable JSON out of a 1B model
&lt;/h3&gt;

&lt;p&gt;My first eval on the Mac looked fine on the surface: 6 out of 6 songs were playable. Underneath, the schema-constrained decoding (MLX's &lt;code&gt;MLXGuidedGeneration&lt;/code&gt;, built on XGrammar) was failing every time, and a fallback parser was quietly rescuing it.&lt;/p&gt;

&lt;p&gt;Logging the partial output showed why. Gemma was emitting newline after newline. JSON allows unlimited whitespace, greedy decoding found that whitespace was always a legal next token, and it filled all 360 tokens with it. Adding the library's whitespace and closing-token biases fixed it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Schema-guided success&lt;/td&gt;
&lt;td&gt;0 / 6&lt;/td&gt;
&lt;td&gt;6 / 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average compose time (M2 Pro)&lt;/td&gt;
&lt;td&gt;9.5 s&lt;/td&gt;
&lt;td&gt;4.4 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The second problem was taste. Gemma picked D major pentatonic, chords 1 5 6 4, 60 bpm almost every time, including for a run. A 1B model leans hard on whichever option it reads first. So the prompt now shuffles the order of every option list, seeded by the current minute, and a small guard folds tempos into a range that fits the activity (a "75 bpm" run becomes 150). After that the same eval produced three keys, four scales and five different progressions in six songs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowing you're outside without a light sensor
&lt;/h3&gt;

&lt;p&gt;An Apple Watch measures time in daylight with its ambient light sensor, and the app reads that from HealthKit when it's there. But I wanted the app to work for anyone with just a phone, and iPhone apps can't read the phone's light sensor.&lt;/p&gt;

&lt;p&gt;So the phone infers it. A minute counts when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the sun is above civil twilight (-6 degrees), computed locally from time and location with the NOAA solar equations, and&lt;/li&gt;
&lt;li&gt;CoreMotion says you're walking, running or cycling, or the GPS fix is accurate to 20 m (usually means open sky) and you're away from home.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Driving never counts. On first launch, CoreMotion's 7-day activity history gives you a week of estimated songs straight away. With a watch, the measured number takes over.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug that would have ended my run
&lt;/h3&gt;

&lt;p&gt;I had a second AI agent review the code before the run. It found a real crash. iOS doesn't let background apps use the GPU. If I tapped Start and locked my phone while Gemma was still composing, MLX would hit that error and call &lt;code&gt;fatalError&lt;/code&gt;, killing the app, the music and the minute counter all at once, a few seconds into the run.&lt;/p&gt;

&lt;p&gt;The fix has three parts. Gemma only runs while the app is on screen, and a request made in the background waits until you open the app again. The screen stays awake for the few seconds composing takes. And every MLX call is wrapped in &lt;code&gt;withError&lt;/code&gt; so a GPU failure becomes a Swift error and falls back to a built-in song instead of a crash. The music starts immediately with the drone either way, and Gemma's song takes over at the next 16-bar phrase boundary so nothing cuts off mid-note.&lt;/p&gt;

&lt;p&gt;The same review caught that run progress was only saved when you tapped End, so it now saves every half minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting the song off the phone
&lt;/h3&gt;

&lt;p&gt;After the run I wanted the song on my laptop, but I'd never exported it. Because the renderer is deterministic, I copied the day's record and the field clip off the phone over a cable and rendered the identical song on the Mac with the same engine. That's the audio linked above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built with an AI agent, tested by running
&lt;/h3&gt;

&lt;p&gt;I built this in one long session with Claude Code: plan, code, test, review, fix. It set up a small harness (&lt;code&gt;make test&lt;/code&gt;, &lt;code&gt;make render&lt;/code&gt;, &lt;code&gt;make eval&lt;/code&gt;, &lt;code&gt;make device&lt;/code&gt;) so every change got checked against unit tests, rendered audio levels, and real Gemma output before it reached my phone. Then the real test was mine: put my shoes on and press Start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Grow a Song needs your location, your movement, sometimes your health data, and a microphone. That's a lot to hand to a server so it can pick a chord progression.&lt;/p&gt;

&lt;p&gt;With an open-weight model running locally, none of that leaves the phone. GPS, motion, daylight minutes and the street recordings stay on the device, and there's no backend that could leak them.&lt;/p&gt;

&lt;p&gt;It also works where runs actually happen. Trails and parks often have no signal, and after the 0.8 GB download the app doesn't need one.&lt;/p&gt;

&lt;p&gt;Running the model costs me nothing, so Gemma can compose before every walk instead of me rationing requests. And because the model is one line of config, I can move to a bigger Gemma once one fits comfortably on a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma 3 1B (QAT, 4-bit) runs fully on device through MLX Swift LM and composes every song with schema-guided JSON output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt;: narration for the demo video. The app itself makes no network calls.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Built with Claude Code. I directed the project, tested it on my own run, and reviewed and edited this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>devchallenge</category>
      <category>ai</category>
      <category>ios</category>
    </item>
    <item>
      <title>pfft...yet another finance tracker? think again :)</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 04 Oct 2026 18:05:20 +0000</pubDate>
      <link>https://dev.to/anshsaini/pfftyet-another-finance-tracker-think-again--5fni</link>
      <guid>https://dev.to/anshsaini/pfftyet-another-finance-tracker-think-again--5fni</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built
&lt;/h2&gt;

&lt;p&gt;Living in India, we heavily utilise UPI for payments. The introduction of UPI, and 10-minute delivery apps have made spending money so easy and comfortable that it has made spendings get a little out of hand. We don't realise how much money we're spending on a day-to-day basis. I noticed my dear friend/colleague Anmol also struggling to keep track of where his money went.&lt;/p&gt;

&lt;p&gt;I personally have been using an Excel tracker for last few years. But I knew Anmol. He wouldn't find it appealing to manually categorize his expenses and create a budget. He's a guy who loves automations. He's the one who introduced me to the iOS Shortcuts app in the past. That got my gears turning. I knew I could use it to trigger an automation whenever a message arrives from a particular sender. I knew Anmol would want something that works seamlessly and requires minimal effort on his part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built a privacy focused finance tracker (pfft...). How it stands out from the rest?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No manual entries required. It automatically reads bank transactions through your text messages.&lt;/li&gt;
&lt;li&gt;Built with privacy in mind. The app never requests access to read all your messages. Just the ones you allow. Using "Automations" in the iOS Shortcuts app. The app only processes the messages you permit, ensuring your financial data remains private.&lt;/li&gt;
&lt;li&gt;Uses AI to auto categorise transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/09ba2353-03a1-4fe8-aa33-bf7e7eacd278" rel="noopener noreferrer"&gt;Click here to watch the video demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fon49v8hzfczub2saa2s9.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%2Fon49v8hzfczub2saa2s9.PNG" alt="iOS Automation Setup" width="800" height="1731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/pfft" rel="noopener noreferrer"&gt;
        pfft
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;pfft&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;P&lt;/strong&gt;rivacy &lt;strong&gt;F&lt;/strong&gt;ocused &lt;strong&gt;F&lt;/strong&gt;inance &lt;strong&gt;T&lt;/strong&gt;racker. A personal finance tracker for India. An iPhone app reads bank SMS (ICICI, Axis), files each transaction into a category and asks you only when it isn't sure. The model that does the filing is open-weight and runs on a Mac at home, so no AI company sees your spending.&lt;/p&gt;

  
    
    &lt;span class="m-1"&gt;demo-compressed.mp4&lt;/span&gt;
  

  

  


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;iPhone (Shortcuts automation on a bank SMS)
   -&amp;gt; iOS app "Log Transaction" intent
   -&amp;gt; POST /api/v1/ingest   (Next.js API on Vercel)
   -&amp;gt; parse the SMS, store it in Supabase
   -&amp;gt; rules and your own filing history first
   -&amp;gt; otherwise ask the model (Tailscale Funnel -&amp;gt; llama.cpp llama-server, Qwen3-4B-Instruct, on a Mac's GPU)
   -&amp;gt; notification: what it was filed as, or "what was this for?"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most transactions never reach the model. A description you've used before, a merchant rule, a payment that recurs every month or a merchant you…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/pfft" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Building this project required several moving parts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;parser: I created message parsers that extract key information such as transaction amount, merchant name, and date from incoming SMS messages.&lt;/li&gt;
&lt;li&gt;tagger: The categorisation is done by Qwen3-4B-Instruct, an open-weight model, served by llama.cpp's llama-server running on my own computer.&lt;/li&gt;
&lt;li&gt;A beautiful iOS App: Let's be honest, liquid glass feels absolutely amazing to use.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Privacy. SMS Transactions are sensitive and valuable data. By keeping the processing local and using open-weight models, users retain control over their financial information without relying on third-party servers.&lt;/p&gt;

&lt;p&gt;My project idea is not unique. There are many products that do this. For example CRED Money. But they are highly invasive. They access your entire banking history, credit records, sometimes even full access to your email account to parse account statements.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New tech.&lt;/strong&gt; This is my first time using an open-weight model. I learned how to set up a local model on my machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud vs Local.&lt;/strong&gt; Running models on the cloud is kinda slow and made me appreciate the speed and efficiency of my local Apple M2 Chip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tunneling.&lt;/strong&gt; First time tunneled my Mac to the cloud. Learned how to securely expose local services using Tailscale Funnel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size matters.&lt;/strong&gt; I tried a 1.7B model to go faster. It read the prompt 2.5 times faster, then ignored the length limit and wrote a massive piece of junk; 75% of the answers were unusable. The 4B model was the smallest one that behaved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting.&lt;/strong&gt; I tried hosting on Render, but the latency was too high for my use case, so I reverted to running the model locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Anmol's Concern
&lt;/h2&gt;

&lt;p&gt;Anmol, just like myself...is also a developer. The first question he asked me was, you're logging my transactions in your database? Privacy my foot!&lt;/p&gt;

&lt;p&gt;I assured him that on-device storage is on its way. I used a cloud database for the initial implementation but even my eventual goal is to keep all the data on-device to maximise privacy. At least for now I've swapped out my Supabase credentials for his. So he can sleep peacefully knowing I don't have eyes on his bank balance 👀&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks to Anmol
&lt;/h2&gt;

&lt;p&gt;Fun fact: Me being a web developer, this project was originally going to be a web app. But Anmol loves the Apple ecosystem. I knew he'd appreciate a clean liquid glass style apple design. He is always showing me examples of various apps in his phone "Look their UI is so clean, so beautiful", he says. So I knew this would be a big factor for him.&lt;br&gt;
I have never built a native iOS app in my lifetime. So thank you Anmol for pushing my boundaries as well with this project.&lt;/p&gt;

&lt;p&gt;Thank you for having the kind of taste that made me rebuild a webhook into a full-fledged iOS app.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>WorkFlow. Your AI Powered Secretary.</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 01 Sep 2024 17:01:00 +0000</pubDate>
      <link>https://dev.to/anshsaini/workflow-your-ai-powered-secretary-4np5</link>
      <guid>https://dev.to/anshsaini/workflow-your-ai-powered-secretary-4np5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/nylas"&gt;Nylas Challenge&lt;/a&gt;: AI Expedition.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built and Why
&lt;/h2&gt;

&lt;p&gt;When you unlock the power of accessing people's email, the possibilities become endless. I've created an assistant which has the email context of not just yourself, but your entire company! So in essence, it can act as a knowledge powerhouse for your entire organization.&lt;/p&gt;

&lt;p&gt;WorkFlow is your ultimate personal secretary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Effortlessly find the best time to meet with anyone.&lt;/li&gt;
&lt;li&gt;Schedule meetings&lt;/li&gt;
&lt;li&gt;Keep track of your daily tasks.&lt;/li&gt;
&lt;li&gt;Instantly see who's on leave and even manage your own leaves.&lt;/li&gt;
&lt;li&gt;Get information about colleagues at your fingertips.&lt;/li&gt;
&lt;li&gt;Access critical company details like employee count, company vision, or who to reach out to for feedback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the cherry on top? It’s Channel Agnostic ✨! Whether it’s WhatsApp, Slack, Discord, or even a website or mobile app, WorkFlow integrates seamlessly with whatever communication platform you’re already using. Just plug it in and let it do the work!&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;To bring this to life, I’ve set up a website where you can interact with the assistant. I’ve also integrated it with WhatsApp to demonstrate just how effortlessly it connects with popular communication platforms.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://workflow-ai-assistant.vercel.app/assistant" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fworkflow-ai-assistant.vercel.app%2Fimages%2Fog.jpg" height="auto" class="m-0"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://workflow-ai-assistant.vercel.app/assistant" rel="noopener noreferrer" class="c-link"&gt;
          WorkFlow
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Your AI Powered Secretary. Delegate your mundane tasks like scheduling meetings, getting task summary, etc to WorkFlow.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fworkflow-ai-assistant.vercel.app%2Ffavicon%2Ffavicon-16x16.png"&gt;
        workflow-ai-assistant.vercel.app
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Here's a video demo
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/aExiKc7WT7Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  WhatsApp Integration
&lt;/h4&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3eYBB4hsvgE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/workflow-assistant" rel="noopener noreferrer"&gt;
        workflow-assistant
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Your AI Powered Secretary.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;WorkFlow. Your AI Powered Secretary.&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/ansh-saini/workflow-assistant/main/public/images/og.jpg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fansh-saini%2Fworkflow-assistant%2Fmain%2Fpublic%2Fimages%2Fog.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I've created an assistant which has the email context of not just yourself, but your entire company! So in essence, it can act as a knowledge powerhouse for your entire organization.&lt;/p&gt;
&lt;p&gt;This assistant is your ultimate personal secretary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Effortlessly find the best time to meet with anyone.&lt;/li&gt;
&lt;li&gt;Schedule meetings&lt;/li&gt;
&lt;li&gt;Keep track of your daily tasks.&lt;/li&gt;
&lt;li&gt;Instantly see who's on leave and even manage your own leaves.&lt;/li&gt;
&lt;li&gt;Get information about colleagues at your fingertips.&lt;/li&gt;
&lt;li&gt;Access critical company details like employee count, company vision, or who to reach out to for feedback.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the cherry on top? It’s Channel Agnostic ✨! Whether it’s WhatsApp, Slack, Discord, or even a website or mobile app, this assistant integrates seamlessly with whatever communication platform you’re already using. Just plug it in and let it do the work!&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/workflow-assistant" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  My Journey
&lt;/h2&gt;

&lt;p&gt;The thing I loved about Nylas was the simplicity of APIs and SDKs. They were intuitive to use. I utilised various Nylas APIs to make the assistant capable of handling complex prompts.&lt;/p&gt;

&lt;p&gt;Development and testing with Nylas was also straightforward. I liked that I could jump straight in using Test Grants. There was no barrier to entry.&lt;/p&gt;

&lt;p&gt;It was a new experience for me to have control of users's email data. This project gave me a perspective on handling sensitive data and writing secure code with good practices.&lt;/p&gt;

&lt;p&gt;This was my first time building an AI Agent. I loved that, through the medium of this challenge, I was able to push the limits of my boundaries and dive into seemingly overwhelming topics like Embeddings and RAGs. I've personally learned A LOT throughout the process of building this project.&lt;/p&gt;

&lt;p&gt;Through this challenge I've come away with knowledge that I am surely going to implement in my personal as well work projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technology &amp;amp; Integrations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.nylas.com/docs/v3/calendar/" rel="noopener noreferrer"&gt;Nylas Calendar APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.nylas.com/docs/v3/calendar/using-the-events-api/" rel="noopener noreferrer"&gt;Nylas Events APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.nylas.com/docs/v3/email/" rel="noopener noreferrer"&gt;Nylas Messages APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/en-us" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Scope and Improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In future, If Nylas rolls out support for Storage access like Google Drive, Microsoft OneDrive, It can easily be integrated into WorkFlow. This will allow users to share files using the assistant.&lt;/li&gt;
&lt;li&gt;Knowledge Store implementation using RAGs. Switching from a traditional database to a vector database will make the the assistant work much more efficiently and scale for large companies with hundreds of thousands of employees.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thank You
&lt;/h2&gt;

&lt;p&gt;A huge shoutout to Nylas and the DEV team for organizing such an incredible challenge! This experience has pushed me out of my comfort zone, allowing me to explore new technologies and grow as a developer.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>nylaschallenge</category>
      <category>api</category>
      <category>ai</category>
    </item>
    <item>
      <title>Free up some space on your VS Code</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 04 Aug 2024 08:35:20 +0000</pubDate>
      <link>https://dev.to/anshsaini/free-up-some-space-on-your-vs-code-2d0g</link>
      <guid>https://dev.to/anshsaini/free-up-some-space-on-your-vs-code-2d0g</guid>
      <description>&lt;p&gt;This is the first of a series of &lt;em&gt;opinionated&lt;/em&gt; posts that I'm going to make about becoming an absolute master of VSCode shortcuts. &lt;/p&gt;

&lt;p&gt;This Activity Bar is just something that takes up screen real estate and honestly its just useless.&lt;/p&gt;

&lt;p&gt;Just setup keyboard shortcuts for the tabs that you use. These are what I use.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cmd + Shift + E&lt;/code&gt;: File Explorer&lt;br&gt;
&lt;code&gt;Cmd + Shift + F&lt;/code&gt;: Find in files (Already the default)&lt;br&gt;
&lt;code&gt;Cmd + Shift + G&lt;/code&gt;: Source Control&lt;br&gt;
&lt;code&gt;Cmd + Shift + A&lt;/code&gt;: Active pull request&lt;br&gt;
&lt;code&gt;Option + Shift + D&lt;/code&gt;: Debug Panel&lt;/p&gt;

&lt;p&gt;For panels which you use often, but not on the daily (for example: the extensions panel), you can just use the command palette. &lt;code&gt;Cmd + Shift + P&lt;/code&gt; and type 'focus on...'.&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%2F90b4vni9h6qcn13ng548.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%2F90b4vni9h6qcn13ng548.png" alt="command palette example" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can safely get rid of it and on the off chance you might need to access the Activity Bar, you can just use the command palette again to bring it back :)&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%2F8ck9yd2w24vihibt2421.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%2F8ck9yd2w24vihibt2421.png" alt="Hide activity bar" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>programming</category>
      <category>developer</category>
    </item>
    <item>
      <title>Finally, a scalable folder structure for your frontend code</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 04 Aug 2024 06:32:41 +0000</pubDate>
      <link>https://dev.to/anshsaini/finally-a-scalable-folder-structure-for-your-frontend-code-43li</link>
      <guid>https://dev.to/anshsaini/finally-a-scalable-folder-structure-for-your-frontend-code-43li</guid>
      <description>&lt;h2&gt;
  
  
  Problem with "pages"
&lt;/h2&gt;

&lt;p&gt;Well, its simple. Not all features are pages. Then you end up developing features in your re-usable components folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;A folder-based structure which aims to keep a feature and its dependencies together. I'll be referring to this structure as &lt;strong&gt;The Base&lt;/strong&gt; in the rest of this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FolderName
├── index.tsx // Entry point
├── FolderName.module.css // Styles
├── components // Components that are specific to this feature
│   ├── ComponentName
│   │   ├── index.tsx
│   │   └── ComponentName.module.css
│   └── AnotherComponentName
│       ├── index.tsx
│       └── AnotherComponentName.module.css
├── hooks // Hooks that are specific to this feature
│   ├── useHookName.ts
│   └── useAnotherHookName.ts
├── utils // Utils that are specific to this feature
│   ├── utilName.ts
│   └── anotherUtilName.ts
└── assets // Assets that are specific to this feature
    └── someImage.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beauty of this structure is that if you don't need something, just don't create the folder for it. &lt;/p&gt;

&lt;p&gt;Here's an example of a react app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src
├── components // Generic re-usable components
│   ├── ui
│   │   ├── Button
│   │   │   ├── index.tsx
│   │   │   └── Button.module.css
│   │   └── TextField
│   │       ├── index.tsx
│   │       └── TextField.module.css
│   └── Navigation
│       ├── Link
│       │   ├── index.tsx
│       │   └── Link.module.css
│       ├── BottomNavigation
│       │   ├── index.tsx
│       │   └── BottomNavigation.module.css
│       ├── TopNavigation
│       │   └── index.tsx
│       └── hooks
│           └── useExcludeShadow.ts
├── features
│   └── Notifications
│       ├── index.tsx
│       ├── Notifications.module.css
│       ├── components
│       │   ├── NotificationRowCard
│       │   │   └── index.tsx
│       │   ├── NotificationsList
│       │   │   ├── index.tsx
│       │   │   └── NotificationsList.module.css
│       │   └── NotificationsPopup
│       │       └── index.tsx
│       └── hooks
│           ├── useNotificationAction.ts
│           ├── useNotifications.ts
│           └── index.tsx
├── api
│   ├── axios.ts
│   └── resources
│       ├── auth
│       │   ├── index.ts
│       │   └── types.ts
│       ├── notifications
│       │   ├── index.ts
│       │   └── types.ts
│       ├── payment
│       │   ├── index.ts
│       │   └── types.ts
│       └── settings
│           ├── index.ts
│           └── types.ts
├── assets
│   ├── lotties
│   │   └── confetti.json
│   ├── icons
│   ├── placeholders
│   │   └── user-avatar-placeholder.svg
│   ├── fonts
│   │   ├── Inter.ttf
│   │   └── Switzer.ttf
│   ├── logos
│   │   ├── logo-primary.svg
│   │   ├── logo-stacked.svg
│   │   └── logo-alternate.svg
│   └── icons
│       ├── apple-icon.svg
│       ├── facebook-icon.svg
│       ├── google-icon.svg
│       ├── linkedin-icon.svg
│       ├── refresh-icon.svg
│       ├── telegram-icon.svg
│       └── whatsapp-icon.svg
├── global
│   ├── AppContext
│   │   ├── context.ts
│   │   └── index.tsx
│   ├── FeatureToggles
│   │   ├── useFeatureToggle.ts
│   │   └── index.tsx
│   └── UserPreferences
│       ├── useUserPreferences.ts
│       └── index.tsx
├── utils
│   ├── routes.ts
│   ├── formatDate.ts
│   └── hooks
│       ├── useArray.ts
│       └── useDeviceOrientation.ts
└── tracking
    ├── googleAnalytics.ts
    ├── firebaseTracking.ts
    └── useTracking.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure has been working and scaling well for us for over 3 years. &lt;br&gt;
Let me know if you guys want a detailed explanation about the api setup. Maybe I'll convert this post into a series of chapters.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>Ready for the iPhone 16?</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Mon, 15 Jul 2024 00:15:33 +0000</pubDate>
      <link>https://dev.to/anshsaini/ready-for-the-iphone-16-4eah</link>
      <guid>https://dev.to/anshsaini/ready-for-the-iphone-16-4eah</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wix"&gt;Wix Studio Challenge &lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Have you seen those long lines in front of stores before a sale is about to begin? That's exactly what I've built using Wix Studio.&lt;/p&gt;

&lt;p&gt;The website showcases a landing page where the upcoming product is revealed. In this case, The iPhone 16 Pro. You can join the queue, you'll be assigned a position in the queue. &lt;br&gt;
When the sale begins, you'll be emailed that its your turn to purchase. &lt;br&gt;
Then you can access the website and make the purchase.&lt;/p&gt;

&lt;p&gt;I've also added multiplayer cursors on the landing page for a sense of 'crowd'. I couldn't implement real-time cursors because I would need to use custom elements to make it work which was against the rules, so I had to compromise and just make some fake ones 😅.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://anshsaini625.wixstudio.io/online-storefront" rel="noopener noreferrer"&gt;Here's the link to the website&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Development Journey
&lt;/h2&gt;

&lt;p&gt;I was thinking of innovation on e-commerce websites, things that came to mind were 3D experiences within the website, or maybe Augmented Reality. I also thought of building a fancy landing page full of animations, but that wouldn't be something innovative in the shopping experience, that would just be a fancy landing page from which you click "Buy Now" and then its a standard checkout process.&lt;/p&gt;

&lt;p&gt;So I thought of making an online store-front for a product launch. People can see each other's cursors moving around and they can reserve their spot in the queue.&lt;/p&gt;

&lt;p&gt;This was my first time using Wix, or any low-code website builder.&lt;br&gt;
I spent a few days consuming wix tutorials and blogs. Exploring &lt;a href="https://dev.wix.com/docs/velo" rel="noopener noreferrer"&gt;Velo documentation&lt;/a&gt; to see what all possibilities are there. The tutorials were really helpful.&lt;/p&gt;

&lt;p&gt;Took me a few days to get the hang of it. It was a nice challenge to accomplish everything "The Wix Way". Using &lt;code&gt;$w&lt;/code&gt; instead of the JavaScript window.&lt;/p&gt;

&lt;p&gt;After the user is authenticated, I save their position in the queue in a Velo Database&lt;/p&gt;

&lt;h3&gt;
  
  
  APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Velo Members API&lt;/li&gt;
&lt;li&gt;Velo Databases and Permissions&lt;/li&gt;
&lt;li&gt;Velo Products&lt;/li&gt;
&lt;li&gt;Velo DOM apis to manipulate DOM elements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Some feedback for Wix
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This was my first time using Wix Studio. The tutorial videos were really helpful, I didn't have to search for the tutorials for each thing I needed to do. They were right there in the platform itself. I loved that.&lt;/li&gt;
&lt;li&gt;The tutorials were well made and covered decent variety of use cases&lt;/li&gt;
&lt;li&gt;Managing store listings is really convenient. Adding products, managing inventory is really intuitive.&lt;/li&gt;
&lt;li&gt;Keyboard shortcuts for hiding/revealing the code editor. Much needed.&lt;/li&gt;
&lt;li&gt;There's no 'onMouseMove' event listener built-in to Velo. It prevented me from implementing real-time multiplayer cursors which I was really excited about. &lt;a href="https://forum.wixstudio.com/t/tracking-mouse-move-with-velo-code/35183" rel="noopener noreferrer"&gt;There is a workaround but it requires custom elements&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>wixstudiochallenge</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Recycling comes to WhatsApp ♻️</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Fri, 21 Jun 2024 03:44:11 +0000</pubDate>
      <link>https://dev.to/anshsaini/recycling-comes-to-whatsapp-63a</link>
      <guid>https://dev.to/anshsaini/recycling-comes-to-whatsapp-63a</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/twilio"&gt;Twilio Challenge v24.06.12&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built 🛠️
&lt;/h2&gt;

&lt;p&gt;A WhatsApp bot which gives you recycling ideas at your fingertips.&lt;br&gt;
WhatsApp is such a popular and convenient platform, that almost everyone has it. Just message the bot and tell it what you want to recycle. &lt;br&gt;
It works with text as well as images 🖼️✨&lt;/p&gt;
&lt;h3&gt;
  
  
  Motivation
&lt;/h3&gt;

&lt;p&gt;Often times while cleaning my house, I come across stuff I don't need anymore. Wouldn't it be nice to just snap a picture with the most used app on my phone and get instructions on how to recycle it? Well, think no more...&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo📱
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ansh-saini/recycle-whatsapp-bot?tab=readme-ov-file#just-tell-it-what-you-want-to-recycle" rel="noopener noreferrer"&gt;Watch the full working demo on GitHub&lt;/a&gt;&lt;br&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%2Fiout7ht5vs0zbqqh4c5s.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%2Fiout7ht5vs0zbqqh4c5s.gif" alt="Working demo" width="222" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently the app is running in a sandbox environment. &lt;br&gt;
Text &lt;code&gt;join want-correctly&lt;/code&gt; to &lt;code&gt;+14155238886&lt;/code&gt; OR scan the QR code below.&lt;br&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%2Fh1tsf4zeqy9eqiiwuljc.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%2Fh1tsf4zeqy9eqiiwuljc.png" alt="QR code to try out the demo" width="452" height="442"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Link to code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/recycle-whatsapp-bot" rel="noopener noreferrer"&gt;
        recycle-whatsapp-bot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This is my submission for the DEV Twilio Challenge
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;WhatsApp Recycle Bot ♻️&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A WhatsApp bot which gives you recycling ideas at your fingertips.
WhatsApp is such a popular and convenient platform, that almost everyone has it. Just message the bot and tell it what you want to recycle.
It works with text as well as images 🖼️✨&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Just tell it what you want to recycle&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/32511936/340074578-5eab6bc1-4f02-482a-b7ca-752ccc336e0d.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ1NzgtNWVhYjZiYzEtNGYwMi00ODJhLWI3Y2EtNzUyY2NjMzM2ZTBkLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWU4NmYwYWU5Mjg3N2FlMGIwYjQxNjNkZjU4ZmQ2MmFiODY4NDVhOWYzMDljMjEwMWIwZThmYzA1NTI1YWQyN2QmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.Ujnxi1puf1cK51JBdkZN9ZdYwapUyVjdBo3X18WvydY"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F32511936%2F340074578-5eab6bc1-4f02-482a-b7ca-752ccc336e0d.gif%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ1NzgtNWVhYjZiYzEtNGYwMi00ODJhLWI3Y2EtNzUyY2NjMzM2ZTBkLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWU4NmYwYWU5Mjg3N2FlMGIwYjQxNjNkZjU4ZmQ2MmFiODY4NDVhOWYzMDljMjEwMWIwZThmYzA1NTI1YWQyN2QmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.Ujnxi1puf1cK51JBdkZN9ZdYwapUyVjdBo3X18WvydY" alt="nailcutter-example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Even works with images!&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/32511936/340074824-11a034e5-acdc-48dc-bbff-39f03e7e95a5.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ4MjQtMTFhMDM0ZTUtYWNkYy00OGRjLWJiZmYtMzlmMDNlN2U5NWE1LmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWNkMTIxYmM4Njc5MDE5YjJkOGRmODY5ZmUwYzZiODFlNjQ5YmJkNzRkYmJjNDYwZDljMGYxZDY4YzNhNzdkOWMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.2iNlQ6gJkksW2z18nhobTnOpVkZTWrRyG_21XIubBqg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F32511936%2F340074824-11a034e5-acdc-48dc-bbff-39f03e7e95a5.gif%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3OTExOTE5MDgsIm5iZiI6MTc5MTE5MTYwOCwicGF0aCI6Ii8zMjUxMTkzNi8zNDAwNzQ4MjQtMTFhMDM0ZTUtYWNkYy00OGRjLWJiZmYtMzlmMDNlN2U5NWE1LmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjEwMDUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYxMDA1VDA5MTMyOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWNkMTIxYmM4Njc5MDE5YjJkOGRmODY5ZmUwYzZiODFlNjQ5YmJkNzRkYmJjNDYwZDljMGYxZDY4YzNhNzdkOWMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.2iNlQ6gJkksW2z18nhobTnOpVkZTWrRyG_21XIubBqg" alt="flask-example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/recycle-whatsapp-bot" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Twilio and AI ✨
&lt;/h2&gt;

&lt;p&gt;I used the &lt;a href="https://www.twilio.com/docs/messaging" rel="noopener noreferrer"&gt;Twilio Messaging API&lt;/a&gt; to setup communication through WhatsApp. I've used &lt;a href="https://cloud.google.com/vision/docs/drag-and-drop" rel="noopener noreferrer"&gt;Google Cloud Vision API&lt;/a&gt; to detect what object the sender has captured. Then I've engineered a prompt and fed that to &lt;a href="https://platform.openai.com/docs/models/gpt-3-5-turbo" rel="noopener noreferrer"&gt;Open AI's GPT 3.5 Turbo&lt;/a&gt; to get instructions on recycling the object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Prize Categories 🏆
&lt;/h2&gt;

&lt;p&gt;Impactful Innovators &lt;/p&gt;

&lt;h2&gt;
  
  
  Some thoughts 🩵
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Working with Twilio has been quite easy. Things are pretty straight forward. I absolutely loved the fact that I didn't have to provide any billing information to get started. &lt;/li&gt;
&lt;li&gt;I was surprised by the simplicity of the steps needed to started with creating a WhatsApp Bot. Great job Twilio! Definitely going to continue using Twilio for building some cool stuff.&lt;/li&gt;
&lt;li&gt;Thank you DEV Team for organising awesome contests like these :)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>twiliochallenge</category>
      <category>ai</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Deadlock</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Thu, 13 Jun 2024 02:57:29 +0000</pubDate>
      <link>https://dev.to/anshsaini/deadlocks-3h3n</link>
      <guid>https://dev.to/anshsaini/deadlocks-3h3n</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for DEV Computer Science Challenge v24.06.12: One Byte Explainer.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explainer
&lt;/h2&gt;

&lt;p&gt;When two or more processes are unable to proceed because each is waiting for another to release a resource. It results in a stalemate where none can progress, requiring intervention like resource reallocation or termination to resolve.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>cschallenge</category>
      <category>computerscience</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A headless hook-based exam proctoring system with React</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Thu, 16 Feb 2023 14:39:25 +0000</pubDate>
      <link>https://dev.to/anshsaini/a-headless-hook-based-exam-proctoring-system-with-react-20do</link>
      <guid>https://dev.to/anshsaini/a-headless-hook-based-exam-proctoring-system-with-react-20do</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I built a headless online exam proctoring system using React.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Force full-screen mode&lt;/li&gt;
&lt;li&gt;Warns when user switches tabs&lt;/li&gt;
&lt;li&gt;Prevents user from opening the context menu (menu opened by pressing right-click from the mouse)&lt;/li&gt;
&lt;li&gt;Prevents user from copying website content&lt;/li&gt;
&lt;li&gt;Works well with custom proctoring logic (which you might want to implement separately)&lt;/li&gt;
&lt;li&gt;Webcam detection (Coming Soon)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Category Submission
&lt;/h2&gt;

&lt;p&gt;SaaS Superstars✨&lt;/p&gt;

&lt;h2&gt;
  
  
  App Link
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://172.104.207.164/" rel="noopener noreferrer"&gt;Checkout the demo app here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Screenshots
&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%2Fexxrz1wk0bh79n2m2zhc.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%2Fexxrz1wk0bh79n2m2zhc.png" alt="Begin Exam" width="800" height="328"&gt;&lt;/a&gt;&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%2Fkattztrgfzhdgi1ktcco.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%2Fkattztrgfzhdgi1ktcco.png" alt="Exam page" width="800" height="753"&gt;&lt;/a&gt;&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%2Fd4xqrld0jd0q9nw20dv7.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%2Fd4xqrld0jd0q9nw20dv7.png" alt="Full screen warning" width="800" height="584"&gt;&lt;/a&gt;&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%2Fjpfik0j7ldxsv15biqck.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%2Fjpfik0j7ldxsv15biqck.png" alt="Tab focus warning" width="800" height="757"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Description
&lt;/h2&gt;

&lt;p&gt;This is a headless proctoring library which only gives you some flags. What you do with that information is totally upto you. The UI for handling various use cases is completely in your hands. For more details, &lt;a href="https://github.com/ansh-saini/react-proctoring#overview" rel="noopener noreferrer"&gt;check out the project README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Link to Source
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/react-proctoring" rel="noopener noreferrer"&gt;
        react-proctoring
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Headless proctoring system for react
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Overview&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This is a headless library which only gives you some flags. What you do with that information is totally upto you. The UI for handling various use cases is completely in your hands.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Features&lt;/h1&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Force full-screen mode&lt;/li&gt;
&lt;li&gt;Warns when user switches tabs&lt;/li&gt;
&lt;li&gt;Prevents user from opening the context menu (menu opened by pressing right-click from the mouse)&lt;/li&gt;
&lt;li&gt;Prevents user from copying website content&lt;/li&gt;
&lt;li&gt;Works well with custom proctoring logic (which you might want to implement separately)&lt;/li&gt;
&lt;li&gt;Webcam detection (Coming Soon)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why should you use this?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;There are lots of browser specific issues that show up when you are working with browser APIs. This package tries to cover most if not all of them. So you can focus on whats actually important for your business. All features are set up in a way that you can opt out of any feature that you do not wish to use.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Why do&lt;/h3&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/react-proctoring" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I was looking for a ready-to-use proctoring system for an online exam. I found a few solutions but all of them were coupled very closely with the UI. I could not find a headless proctoring system. So, I decided to build my own. It is perfect for having full control over the UI while reaping all the benefits of a proctoring system.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I built it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I utilized Linode's web server to deploy the demo application to the web. I had never configured a web server before. I learnt how to do that from Linode's awesome documention.&lt;/li&gt;
&lt;li&gt;I'm thinking of adding a face detection feature to this library. I'm planning to host machine learning models on Linode then use it as an API in this library.&lt;/li&gt;
&lt;li&gt;I learned about various browser APIs. Like page visibility detection, requesting camera permissions, requesting full screen permission from the browser etc.&lt;/li&gt;
&lt;li&gt;I learned about ways of getting around cross-browser issues when it comes to native APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;This was my first time using Linode. Things were very easy to setup. I followed &lt;a href="https://www.linode.com/docs/guides/how-to-deploy-a-react-app-on-debian-10/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to understand how to deploy my application. I think Linode is a great platform and I'm definitely going to figure out how to utilize my free $100 credit in a better way 😁&lt;/p&gt;

</description>
      <category>mentorship</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to download fancy QR Codes with React</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Sun, 08 Jan 2023 10:48:45 +0000</pubDate>
      <link>https://dev.to/anshsaini/how-to-download-fancy-qr-codes-with-react-b25</link>
      <guid>https://dev.to/anshsaini/how-to-download-fancy-qr-codes-with-react-b25</guid>
      <description>&lt;p&gt;Generating QR codes on the web is easy. There are lots of libraries out there who help us do it. One of them is &lt;a href="https://www.npmjs.com/package/qrcode.react" rel="noopener noreferrer"&gt;qrcode.react&lt;/a&gt; which we will be using for the purpose of this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code to download a plain QR Code
&lt;/h2&gt;

&lt;p&gt;We're rendering a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element in the DOM using &lt;code&gt;QRCodeCanvas&lt;/code&gt; supplied by &lt;code&gt;qrcode.react&lt;/code&gt;. We can convert it to a &lt;code&gt;png&lt;/code&gt; using JavaScript and then download it as a file.&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;// QRCodeDownload.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;QRCodeCanvas&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qrcode.react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/Button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt; &lt;span class="o"&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="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://anshsaini.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;downloadQRCode&lt;/span&gt; &lt;span class="o"&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="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#qrcode-canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLCanvasElement&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="nx"&gt;canvas&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="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;canvas&amp;gt; not found in the DOM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pngUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&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;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/octet-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;downloadLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pngUrl&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;QR code.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"p-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeCanvas&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"qrcode-canvas"&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"H"&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;downloadQRCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Download QR Code&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fkuu7f96ktpksg5a4sdv6.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%2Fkuu7f96ktpksg5a4sdv6.png" alt="QR Code downloaded" width="734" height="962"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila✨ The QR Code is now saved in the file system as an image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking it a step further
&lt;/h2&gt;

&lt;p&gt;No one wants to share a QR code on its own. Its ugly. We want a nice UI surrounding the QR code, giving it some context. Maybe something like this:&lt;br&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%2Ffb4o9tc6jad4vfuctig0.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%2Ffb4o9tc6jad4vfuctig0.png" alt="Fancy QR Code" width="556" height="810"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's a create a wrapper component which will handle the styling. I'm using &lt;code&gt;react-jss&lt;/code&gt; for styling but of course you can use whatever you want and style your QR Code however you like.&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;// QRCodeTemplate.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DEV_RAINBOW_LOGO&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assets/qr_code/dev-rainbow.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DEV_RAINBOW_BG&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assets/qr_code/dev-rainbow-bg.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createUseStyles&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-jss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Typography&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/Typography&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUseStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;backgroundImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`url('&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;DEV_RAINBOW_BG&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;')`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1060&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1521&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;32px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;5px solid #EFEFF7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9px 3px 20px 1px rgb(0 0 0 / 10%)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&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="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&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="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Need to offset the values due to `excavate: true` in qrcode.react&lt;/span&gt;
    &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`calc(50% - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Need to offset the values due to `excavate: true` in qrcode.react&lt;/span&gt;
    &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`calc(50% - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;qrContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#EFEFF7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;56px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spacing&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;qrInner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;32px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;referredBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;600&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;29px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;letterSpacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0.1em&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="nx"&gt;classes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStyles&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fancy-qr-code"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"logo"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DEV_RAINBOW_LOGO&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mt-7"&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"title1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        To register, scan the QR Code.
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrContainer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;
          &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DEV_RAINBOW_LOGO&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ICON_SIZE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrInner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referredBy&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"emphasised"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        REFERRED BY
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"largeTitle"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Ansh Saini&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;QRCodeTemplate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now pass our QR Code as children to the &lt;code&gt;QRCodeTemplate&lt;/code&gt;&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeCanvas&lt;/span&gt;
    &lt;span class="c1"&gt;// Passing image here doesn't render when we convert html to canvas. So, we handle it manually.&lt;/span&gt;
    &lt;span class="c1"&gt;// We are only using this prop for `excavate: true` use case.&lt;/span&gt;
    &lt;span class="na"&gt;imageSettings&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;excavate&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="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"H"&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Earlier we only had a canvas element in the DOM so we could directly convert it to a &lt;code&gt;png&lt;/code&gt;. But now, our DOM includes HTML elements like &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; etc. for styling. So we need to parse our DOM as &lt;code&gt;canvas&lt;/code&gt; and then convert it into a &lt;code&gt;png&lt;/code&gt;. There's a libary which helps us do just that. &lt;a href="https://html2canvas.hertzen.com/" rel="noopener noreferrer"&gt;html2canvas&lt;/a&gt;. We modify our &lt;code&gt;downloadQRCode&lt;/code&gt; function such that first we get snapshot of our DOM as a &lt;code&gt;canvas&lt;/code&gt; then we convert that into a &lt;code&gt;png&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// QRCodeTemplate.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;html2canvas&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html2canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;QRCodeCanvas&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qrcode.react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/Button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QRCodeTemplate&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/dashboard/QRCodeTemplate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt; &lt;span class="o"&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="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://anshsaini.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCanvas&lt;/span&gt; &lt;span class="o"&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="nx"&gt;qr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fancy-qr-code&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;qr&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;return&lt;/span&gt; &lt;span class="nf"&gt;html2canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;onclone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;snapshot&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="nx"&gt;qrElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fancy-qr-code&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;qrElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="c1"&gt;// Make element visible for cloning&lt;/span&gt;
        &lt;span class="nx"&gt;qrElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;downloadQRCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="nx"&gt;canvas&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;getCanvas&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="nx"&gt;canvas&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="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;canvas&amp;gt; not found in DOM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pngUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&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;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/octet-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;downloadLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pngUrl&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;QR code.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;downloadLink&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;downloadLink&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"p-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QRCodeCanvas&lt;/span&gt;
          &lt;span class="c1"&gt;// Passing image here doesn't render when we convert html to canvas. So, we handle it manually.&lt;/span&gt;
          &lt;span class="c1"&gt;// We are only using this prop for `excavate: true` use case.&lt;/span&gt;
          &lt;span class="na"&gt;imageSettings&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;excavate&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="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"H"&lt;/span&gt;
          &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QRCodeTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;downloadQRCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Download QR Code&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;QRCodeDownload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;We have a beautiful QR code saved as a &lt;code&gt;png&lt;/code&gt;. And with the power of React, we can create as many styles as we want.&lt;br&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%2F7qy22fyjzjhuu85lkecg.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%2F7qy22fyjzjhuu85lkecg.png" alt="Fancy QR Code downloaded" width="573" height="967"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Understanding stops property on LinearGradient in Flutter</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Thu, 05 Jan 2023 05:53:31 +0000</pubDate>
      <link>https://dev.to/anshsaini/understanding-stops-property-on-lineargradient-in-flutter-1fnk</link>
      <guid>https://dev.to/anshsaini/understanding-stops-property-on-lineargradient-in-flutter-1fnk</guid>
      <description>&lt;p&gt;This is what the &lt;a href="https://api.flutter.dev/flutter/painting/Gradient/stops.html" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; say&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A list of values from 0.0 to 1.0 that denote fractions along the gradient.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Lets say we have to create a bar which has a gradient going from red -&amp;gt; green -&amp;gt; blue -&amp;gt; black.&lt;br&gt;
The percentages are as follows:&lt;br&gt;
10% red&lt;br&gt;
30% green&lt;br&gt;
40% blue&lt;br&gt;
Rest of it black (20%)&lt;/p&gt;

&lt;p&gt;We might write something like this:&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="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&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;Here's what we get:&lt;br&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%2Fbol9p0kqqywcxsyyz3a5.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%2Fbol9p0kqqywcxsyyz3a5.png" alt="Incorrect gradient" width="800" height="112"&gt;&lt;/a&gt;Of course this is not what we want.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to fix it?
&lt;/h3&gt;

&lt;p&gt;Before we jump into the solution let's look at the problematic code&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="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.2&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;We have supplied &lt;code&gt;stops=[0.1, 0.4, 0.5, 0.2]&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  How the &lt;code&gt;stops&lt;/code&gt; property works
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;stops&lt;/code&gt; property requires us to specify where the color should start from. So, if we have two colors, red and black and we want both of them to take 50% of the container, we can write&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="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&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="mf"&gt;0.6&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;Notice that I wrote &lt;code&gt;0.6&lt;/code&gt; instead of &lt;code&gt;0.5&lt;/code&gt;. If we write &lt;code&gt;0.5&lt;/code&gt;, then it would look like a 40-60 split instead of 50-50. &lt;/p&gt;

&lt;p&gt;0 to 0.5 would be red, then 0.6 to 1 would be black.&lt;br&gt;
The following code would also give us the same result&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="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&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 is because &lt;a href="https://api.flutter.dev/flutter/painting/Gradient/stops.html#:~:text=If%20the%20first%20value%20is%20not%200.0%2C%20then%20a%20stop%20with%20position%200.0%20and%20a%20color%20equal%20to%20the%20first%20color%20in%20colors%20is%20implied" rel="noopener noreferrer"&gt;as the docs mention&lt;/a&gt;, if the &lt;code&gt;stops&lt;/code&gt; array doesn’t start with a &lt;code&gt;0&lt;/code&gt;, it implicitly means that it would start with &lt;code&gt;0&lt;/code&gt; and the first color in the &lt;code&gt;colors&lt;/code&gt; array. Same thing happens with last value of &lt;code&gt;stops&lt;/code&gt; array. It should either explicitly end with a &lt;code&gt;1&lt;/code&gt;, or flutter will implicitly consider it as &lt;code&gt;1&lt;/code&gt; with last color supplied in the &lt;code&gt;colors&lt;/code&gt; array. So, actually flutter will interpret it as:&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="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&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="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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;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%2Fyt49oxvheude5iaub7js.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%2Fyt49oxvheude5iaub7js.png" alt="Gradient Examples" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the issue
&lt;/h2&gt;

&lt;p&gt;Now that we know how the &lt;code&gt;stops&lt;/code&gt; property works, we can get our desired gradient.&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="n"&gt;LinearGradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;colors:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;stops:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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;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%2Fgbdllup5oq422euoi0z5.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%2Fgbdllup5oq422euoi0z5.png" alt="Gradient fixed" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Playground
&lt;/h2&gt;

&lt;p&gt;I made a &lt;a href="https://dartpad.dev/?id=9c577bf3dcebf1a5aae37a5cac281761" rel="noopener noreferrer"&gt;dartpad link&lt;/a&gt; if you want to play around with the examples above.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>beginners</category>
      <category>dart</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Multiplayer Logo Quiz? 😳</title>
      <dc:creator>Ansh Saini</dc:creator>
      <pubDate>Tue, 10 May 2022 18:53:21 +0000</pubDate>
      <link>https://dev.to/anshsaini/multiplayer-logo-quiz-16hn</link>
      <guid>https://dev.to/anshsaini/multiplayer-logo-quiz-16hn</guid>
      <description>&lt;h2&gt;
  
  
  Overview of My Submission
&lt;/h2&gt;

&lt;p&gt;Ever played Logo Quiz? Its a game where you see a logo and have to guess which brand owns that logo. I've played it a lot but have always felt that it would be more fun if I could play against my friends. That's exactly what "PvP Logo Quiz" is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submission Category:
&lt;/h2&gt;

&lt;p&gt;🃏 Wacky Wildcards&lt;/p&gt;

&lt;h2&gt;
  
  
  Link to Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ansh-saini" rel="noopener noreferrer"&gt;
        ansh-saini
      &lt;/a&gt; / &lt;a href="https://github.com/ansh-saini/pvp-logo-quiz" rel="noopener noreferrer"&gt;
        pvp-logo-quiz
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Play Logo Quiz with Friends!
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;PVP Logo Quiz&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/pvp-logo-quiz/blob/main/public/Cover.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fansh-saini%2Fpvp-logo-quiz%2Fraw%2Fmain%2Fpublic%2FCover.png" alt="Cover"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Project setup guide&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install and run Appwrite.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a project on Appwrite.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a Web Platform to your project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Head to API Keys section and create a new appwrite server key with the following permissions
The command will fail if you have not provided the necessary permissions.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;users.read, users.write, collections.read, collections.write, attributes.read, attributes.write, indexes.read, indexes.write, documents.read, documents.write&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ansh-saini/pvp-logo-quizpublic/docs/key-permissions.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fansh-saini%2Fpvp-logo-quizpublic%2Fdocs%2Fkey-permissions.png" alt="Appwrite server API key permissions"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone this repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a copy of &lt;code&gt;.env.example&lt;/code&gt; file. Rename it to &lt;code&gt;.env&lt;/code&gt; and fill out the variables. You can find values for &lt;code&gt;APPWRITE_ENDPOINT, APPWRITE_PROJECT&lt;/code&gt; on the Appwrite dashboard itself. &lt;code&gt;APPWRITE_SERVER_API_KEY&lt;/code&gt; is the server API key secret that we generated. Rest of the variables will be generated by the setup script.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;yarn setup&lt;/code&gt;. It will create the required collections and provide you with their unique IDs. Add them to your &lt;code&gt;.env&lt;/code&gt; file. It will also add some logos to your database so you can get up and…&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ansh-saini/pvp-logo-quiz" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Link to Website
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://pvp-logo-quiz.vercel.app/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpvp-logo-quiz.vercel.app%2FCover.png" height="auto" class="m-0"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://pvp-logo-quiz.vercel.app/" rel="noopener noreferrer" class="c-link"&gt;
          PvP Logo Quiz
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Play Logo Quiz with Friends!
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpvp-logo-quiz.vercel.app%2Ffavicon.ico"&gt;
        pvp-logo-quiz.vercel.app
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Additional Resources / Info
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔥 &lt;a href="https://appwrite.io/" rel="noopener noreferrer"&gt;Appwrite&lt;/a&gt; 🔥 (Backend)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; (Frontend + Backend)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt; + &lt;a href="https://www.vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; (Deployment)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Demo &amp;amp; Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/B7wFUADFZMA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Log in
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2F05jpsfy94ciecojbl8et.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F05jpsfy94ciecojbl8et.png" alt="Login Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Sign up
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6adpdfw446q692wmv10z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6adpdfw446q692wmv10z.png" alt="Sign up"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  How to play
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fz5qnyy9i5lqznnfrq2ok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fz5qnyy9i5lqznnfrq2ok.png" alt="How to play"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Home
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ffpp5e4j7ifpfl955q0xb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffpp5e4j7ifpfl955q0xb.png" alt="Home"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Join Room
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2F21milvf295bh21ym5y3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F21milvf295bh21ym5y3d.png" alt="Join Room"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Share Room Code
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ffczzviaps22fbhp5xwzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffczzviaps22fbhp5xwzl.png" alt="Share Room Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Round in progress
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ft8rdec9ez2gt5d5gteth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ft8rdec9ez2gt5d5gteth.png" alt="Round in progress"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.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%2F1xi1colkc8eonvohzier.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F1xi1colkc8eonvohzier.png" alt="Round in progress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Result
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftp64xt944n0r9b7kilua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftp64xt944n0r9b7kilua.png" alt="Result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;After creating an account or logging in on "PvP Logo Quiz", you can create a room and share the room code with your friends.&lt;/li&gt;
&lt;li&gt;The game will start when all players have joined the room.&lt;/li&gt;
&lt;li&gt;Players will be presented with 10 questions (logos). The first player to get the most questions right wins.&lt;/li&gt;
&lt;li&gt;You only get three seconds to answer the question.&lt;/li&gt;
&lt;li&gt;After the game ends for all players, you can see various stats like who won the game and how many questions each player got right and how much time they took overall.&lt;/li&gt;
&lt;li&gt;Every thing is ✨Realtime✨. You can see you opponent's progress. And they can see yours ;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Future Scope
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✨&lt;strong&gt;Matchmaking&lt;/strong&gt;✨. This will allow players to play with anyone in the world without needing to share an invite code.&lt;/li&gt;
&lt;li&gt;Ability to have more than 2 players in a room.&lt;/li&gt;
&lt;li&gt;Play as guest (No need to signup)&lt;/li&gt;
&lt;li&gt;Ability to set number of questions per game.&lt;/li&gt;
&lt;li&gt;Ability to play again in the same room.&lt;/li&gt;
&lt;li&gt;Categories filter. Play a game with only a specific category of logos.&lt;/li&gt;
&lt;li&gt;Mobile version so players can play on the go. 😁&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Setup Guide
&lt;/h3&gt;

&lt;p&gt;I have created a setup script. Just run one command and everything will be set up for you :).&lt;br&gt;
See project &lt;a href="https://github.com/ansh-saini/pvp-logo-quiz/blob/main/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Appwrite features used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Realtime&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;li&gt;Accounts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Thoughts about Appwrite
&lt;/h3&gt;

&lt;p&gt;This was my first time using Appwrite. The tool is pretty awesome. It does so many things behind the scenes that would've taken ages to set up from scratch. Had an amazing time using Appwrite. However, there are a few things that I struggled with. &lt;strong&gt;Please please please let me know if there was something that I missed in the documentation which would have helped me with my problems&lt;/strong&gt;. In no way I am trying to criticize Appwrite. This is just my personal feedback from using Appwrite to build this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I would've liked to have an option to list all documents of a collection. The limit of 100 seems like a good default. But it would be nice to be able to override it beyond 100.&lt;/li&gt;
&lt;li&gt;Support for JSON field
However, if I turn on Array mode for a string attribute, I can send a JavaScript Object as payload. It gets accepted but doesn’t show up properly in Appwrite admin. But when you retrieve it using the API, the data comes as it is. :)&lt;/li&gt;
&lt;li&gt;It was a bit unclear to me how to verify the user's identity on my Next.js server. Reached out on Appwrite discord and someone told me that I needed to generate a JWT for the user and send it to my backend then I can perform actions on behalf of the user. I have personally never used this way for authorizing a user. It felt a little cumbersome.&lt;/li&gt;
&lt;li&gt;Can’t use &lt;code&gt;Query()&lt;/code&gt; with Users API :/
If I want to fetch data of two users, I have to write 2 get queries with their ID. I can not use the list API.&lt;/li&gt;
&lt;li&gt;I accidentally exceeded the size limit of a string field. I got a very vague error (Generic 500 saying 'Internal Server Error') from which I was not able to figure out that I exceeding the size limit. I had to do some trial-and-error to figure out the root cause.&lt;/li&gt;
&lt;li&gt;I was adding attributes to my collection from Appwrite admin. I was not able to add many attributes with a high size. This is not specified anywhere in the docs that I cannot add multiple high-value size fields. I got an error saying "Attribute limit reached." when I tried to add 2 fields with size=10000
&lt;img src="https://media.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%2Ffjlagmyl0fatv3j3mrsl.png" alt="Size limit reached error"&gt; &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db.getIndex&lt;/code&gt; function throws exception even when index exists. The API Key does have &lt;code&gt;indexes.read&lt;/code&gt; and &lt;code&gt;index.write&lt;/code&gt; permission.
```js
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(await database.getCollection("6277923110f6559c7549")).indexes&lt;br&gt;
// Response of getCollection&lt;br&gt;
[&lt;br&gt;
  {&lt;br&gt;
    key: 'name',&lt;br&gt;
    type: 'unique',&lt;br&gt;
    status: 'available',&lt;br&gt;
    attributes: [ 'name' ],&lt;br&gt;
    orders: [ 'ASC' ]&lt;br&gt;
  }&lt;br&gt;
]&lt;/p&gt;

&lt;p&gt;// getIndex does not work on the same collection&lt;br&gt;
await database.getIndex("6277923110f6559c7549", "name")&lt;br&gt;
// Response of getIndex&lt;br&gt;
AppwriteException [Error]: Index not found&lt;br&gt;
    at Client.call (\node_modules\node-appwrite\lib\client.js:162:31)&lt;br&gt;
    at processTicksAndRejections (node:internal/process/task_queues:96:5)&lt;br&gt;
    at async Database.getIndex (\node_modules\node-appwrite\lib\services\database.js:1046:16)&lt;br&gt;
    at async x (\setup.js:51:15) {&lt;br&gt;
  code: 404,&lt;br&gt;
  type: 'index_not_found',&lt;br&gt;
  response: {&lt;br&gt;
    message: 'Index not found',&lt;br&gt;
    code: 404,&lt;br&gt;
    type: 'index_not_found',&lt;br&gt;
    version: '0.13.4'&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;



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

This was my first time participating solo in a hackathon. Thank you so much Dev and Appwrite for this opportunity. I learned a lot of new things while building out this project. It was a lot of fun. Looking forward to using Appwrite in future projects.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>appwritehack</category>
      <category>gamedev</category>
      <category>javascript</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
