{
    "version": "https://jsonfeed.org/version/1",
    "title": "Zwyx's blog",
    "home_page_url": "https://zwyx.dev/blog",
    "description": "Web dev and stuff",
    "items": [
        {
            "id": "https://zwyx.dev/blog/docker-dev",
            "content_html": "<p>Run a <strong>single Docker container</strong> that stays active, and drop into it whenever you need to work on a Node project. This \"system-wide\" container:</p>\n<ul>\n<li class=\"\"><strong>starts once</strong> and stays running: no startup time and less disk space, compared to regular per-project dev containers,</li>\n<li class=\"\"><strong>is used to work on multiple projects</strong>: attach as many VS Code windows as you want to it,</li>\n<li class=\"\"><strong>isolates NPM packages</strong> from your host system.</li>\n</ul>\n<!-- -->\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>Motivation</div><div class=\"admonitionContent_BuS1\"><p>NPM supply chain attacks are increasingly common. Recently, the multiple <a href=\"https://www.koi.ai/incident/shai-hulud-npm-supply-chain-attack-crowdstrike-tinycolor\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Shai-Hulud</a> campaigns compromised many packages including some belonging to CrowdStrike, Zapier, PostHog, Postman, and <a href=\"https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">many others</a>.</p><p>Node.js hasn't been built with security in mind: any script, any dependency, has full access to the user's environment (file system, network, etc. which easily leads to credentials theft). Node's author himself recognised this and created <a href=\"https://deno.com/learn/nodes-security-problem\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Deno and its system of permissions</a> for this reason.</p><p>A way to make working with Node more secure is to use a <a href=\"https://code.visualstudio.com/docs/devcontainers/containers\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">dev container</a>: a Docker container inside which we run all our NPM commands and Node scripts. This isolates them from our host machine and drastically limits the damage in case of compromise.</p><p>However, with many projects, creating one dev container for each can quickly become cumbersome.</p><p><strong>The solution presented here is to create a single, persistent, \"system-wide\" development container, inside which we will open all our projects.</strong></p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"setup\">Setup<a href=\"https://zwyx.dev/blog/docker-dev#setup\" class=\"hash-link\" aria-label=\"Direct link to Setup\" title=\"Direct link to Setup\" translate=\"no\">​</a></h2>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>Note</div><div class=\"admonitionContent_BuS1\"><p>Working inside a dev container doesn't create any overhead on Linux, as Docker works natively there. If you use another system, your experience might be different.</p></div></div>\n<p>The full implementation is called Docker Dev and is available at <a href=\"https://github.com/zwyx/docker-dev\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">github.com/zwyx/docker-dev</a>.</p>\n<p>First, you'll need to set up <a href=\"https://docs.docker.com/engine/install/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Docker</a> if you don't have it already.</p>\n<p>Then, clone the Docker Dev repository:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> clone https://github.com/zwyx/docker-dev.git</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token builtin class-name\">cd</span><span class=\"token plain\"> docker-dev</span><br></span></code></pre></div></div>\n<p>Open the file <code>docker-compose.yml</code>, and modify the following line:</p>\n<div class=\"language-yml codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-yml codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">-</span><span class=\"token plain\"> ~/dev</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\">/home/$</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\">USER</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\">/dev</span><br></span></code></pre></div></div>\n<p>to replace <code>dev</code> with the name of your main workspace folder (containing all your repositories). The goal is for all your projects to have the <strong>same path on your host machine and inside the container</strong>. This will make <strong>switching from any directory on your host to the same directory inside the container</strong> very easy.</p>\n<p>Do the same in the <code>Dockerfile</code>:</p>\n<div class=\"language-dockerfile codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-dockerfile codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token instruction keyword\" style=\"color:#00009f\">WORKDIR</span><span class=\"token instruction\"> /home/</span><span class=\"token instruction variable\" style=\"color:#36acaa\">${USERNAME}</span><span class=\"token instruction\">/dev</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>If you're on a Mac, also replace <code>/home</code> with <code>/Users</code> everywhere in these two files.</p></div></div>\n<p>Now, start the container:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">docker-compose</span><span class=\"token plain\"> up </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-d</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>Note</div><div class=\"admonitionContent_BuS1\"><p>This container is quite tied to my personal configuration and preferences. If something doesn't work, open <code>Dockerfile</code> and <code>docker-compose.yml</code> to make any necessary adjustments.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"terminal-integration\">Terminal integration<a href=\"https://zwyx.dev/blog/docker-dev#terminal-integration\" class=\"hash-link\" aria-label=\"Direct link to Terminal integration\" title=\"Direct link to Terminal integration\" translate=\"no\">​</a></h2>\n<p>On your host machine, add this alias to your <code>~/.zshrc</code> (or equivalent):</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token builtin class-name\">alias</span><span class=\"token plain\"> </span><span class=\"token assign-left variable\" style=\"color:#36acaa\">d</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token string\" style=\"color:#e3116c\">'docker exec -it -w \"$PWD\" dev zsh'</span><br></span></code></pre></div></div>\n<p>Now, if you have correctly configured your workspace folder during the previous step, you can navigate to any project or subdirectory inside it, and simply run:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">d</span><br></span></code></pre></div></div>\n<p>to open a <strong>terminal session inside the dev container, at the same location</strong>:</p>\n<div class=\"imageWrapper_u91s withLegend_q8O0\"><div style=\"border-radius:5px\" class=\"frame_zT4L\"><img style=\"border-radius:5px\" class=\"image_Y_cJ\" src=\"data:image/webp;base64,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\" alt=\"Terminal session inside our dev container.\"></div><div class=\"legend_xj0V\">Docker Dev is preconfigured with ZSH and a theme allowing to instantly recognise that a terminal session is running inside the container.</div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"vs-code-integration\">VS Code integration<a href=\"https://zwyx.dev/blog/docker-dev#vs-code-integration\" class=\"hash-link\" aria-label=\"Direct link to VS Code integration\" title=\"Direct link to VS Code integration\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"attach-the-first-window\">Attach the first window<a href=\"https://zwyx.dev/blog/docker-dev#attach-the-first-window\" class=\"hash-link\" aria-label=\"Direct link to Attach the first window\" title=\"Direct link to Attach the first window\" translate=\"no\">​</a></h3>\n<p>Install the official <a href=\"https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Dev Containers</a> extension.</p>\n<p>Then in VS Code, open the command palette, run <strong>Dev Containers: Attach to Running Container...</strong> and select the container <code>dev</code>.</p>\n<p>Now open a project's folder in this window, and that's it: you can start working with the same dev environment you're used to.</p>\n<p>Some VS Code extensions, like Prettier and ESLint, need to run inside the container. Simply open the Extensions view in your sidebar and click <strong>Install in container</strong> on the extensions you wish to use.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>Note</div><div class=\"admonitionContent_BuS1\"><p>Having <strong>extensions running inside the container is a great security improvement</strong> because VS Code extensions, like NPM packages, <a href=\"https://www.koi.ai/blog/1-6-how-we-hacked-multi-billion-dollar-companies-in-30-minutes-using-a-fake-vscode-extension\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">have full access to your system and can be compromised</a>. Even worse: VS Code, by default, auto-updates them! Which means, if an extension gets compromised, it could land on your machine without you doing anything.</p><p>So having extensions running inside the container is much safer: compromised extensions won't have access to your host system.</p><p>However: <strong>not all extensions run inside the container</strong>. Some extensions, like themes, always run on the UI on your host machine, so they do have access to your system. I highly recommend to <strong>disable auto-update for these extensions</strong>, and to update them manually when nothing is going on in the news.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"attach-multiple-windows\">Attach multiple windows<a href=\"https://zwyx.dev/blog/docker-dev#attach-multiple-windows\" class=\"hash-link\" aria-label=\"Direct link to Attach multiple windows\" title=\"Direct link to Attach multiple windows\" translate=\"no\">​</a></h3>\n<p>You can have as many projects as you want opened simultaneously, each on its own VS Code window attached to the container.</p>\n<p>However, if a VS Code window is already attached to the container, then running again the command <strong>Dev Containers: Attach to Running Container...</strong> will simply focus the existing window, which can be confusing.</p>\n<p>To make things simpler, set the VS Code setting <code>window.openFoldersInNewWindow</code> to <code>on</code>.</p>\n<p>Now, in a window that is already attached to the container, run the command <strong>File: Open Folder...</strong>, select the folder of another project you'd like to open, and press Enter. A new window – also attached to the container – will open with this project.</p>\n<p>Set a keyboard shortcut for this command, for convenience. (I set <code>Ctrl+O</code>, as I only use <code>Ctrl+P</code> to open files.)</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"visual-differentiation\">Visual differentiation<a href=\"https://zwyx.dev/blog/docker-dev#visual-differentiation\" class=\"hash-link\" aria-label=\"Direct link to Visual differentiation\" title=\"Direct link to Visual differentiation\" translate=\"no\">​</a></h3>\n<p>You can decorate windows that are attached to the container differently than regular windows, to quickly distinguish them.</p>\n<p>For instance, you can make the window's title start with a package 📦 icon, and colour its title bar:</p>\n<div class=\"imageWrapper_u91s withLegend_q8O0\"><div style=\"border-radius:5px\" class=\"frame_zT4L\"><img style=\"border-radius:5px\" class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/vscodeWindowCustomisation-d44399addd12664cc250f8f1b1ea5715.webp\" alt=\"Two VS Code windows with different customisation to differentiate containerised applications.\" width=\"500\"></div><div class=\"legend_xj0V\"><span><code>docker-dev</code> is a regular window, <code>zwyx.dev</code> is attached to the container</span></div></div>\n<p>Open VS Code's settings and select the <strong>Remote</strong> tab. Settings in this tab apply only to windows that are attached to the container:</p>\n<div class=\"imageWrapper_u91s\"><div style=\"border-radius:5px\" class=\"frame_zT4L\"><img style=\"border-radius:5px\" class=\"image_Y_cJ\" src=\"data:image/webp;base64,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\" alt=\"VS Code allows to customise settings inside a dev container.\"></div></div>\n<p>These settings are saved in <code>~/.vscode-server/data/Machine/settings.json</code> in the container's file system. Docker Dev includes an example copy of this file, which is copied to the correct location during the container's creation (see the <code>Dockerfile</code>).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"ports\">Ports<a href=\"https://zwyx.dev/blog/docker-dev#ports\" class=\"hash-link\" aria-label=\"Direct link to Ports\" title=\"Direct link to Ports\" translate=\"no\">​</a></h3>\n<p>Now that you have your container, you'll want to run everything Node-related inside it (<code>npm i</code>, <code>npm run dev</code>, etc.).</p>\n<p>To run web applications, you'll need to expose their ports to be able to access them from your browser on your host machine.</p>\n<p>We could configure the ports in the <code>docker-compose.yml</code> file, but exposing ports this way can only be done before the container starts. It cannot be changed while it is running.</p>\n<p>A more practical solution is to <strong>use VS Code to forward our ports</strong>. Contrary to Docker, VS Code can start and stop forwarding ports while the container is running.</p>\n<p>Open the command palette and run <strong>Forward a port</strong>, enter the port number, and press Enter. For example, I am forwarding port <code>3000</code> to run Docusaurus as I'm writing this blog post, to preview it in my browser.</p>\n<p>Note that some dev servers require an additional argument for container networking, usually <code>--host 0.0.0.0</code>. Docker Dev is configured with a few useful aliases, such as:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token builtin class-name\">alias</span><span class=\"token plain\"> </span><span class=\"token assign-left variable\" style=\"color:#36acaa\">nrd</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token string\" style=\"color:#e3116c\">'npm run dev -- --host 0.0.0.0'</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"trash\">Trash<a href=\"https://zwyx.dev/blog/docker-dev#trash\" class=\"hash-link\" aria-label=\"Direct link to Trash\" title=\"Direct link to Trash\" translate=\"no\">​</a></h3>\n<p>On your host machine, deleting a file from VS Code's File Explorer usually sends this file to your system's trash.</p>\n<p>Inside a container however, VS Code isn't able to do this by default, and deletes files permanently instead.</p>\n<p>If you want to have the trash functionality inside your container, <strong>install the <a href=\"https://marketplace.visualstudio.com/items?itemName=Zwyx.remotetrash\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Remote Trash</a> extension</strong>. (Docker Dev is already configured with <code>trash-cli</code>, which is required for this extension to work.)</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"nvm\">NVM<a href=\"https://zwyx.dev/blog/docker-dev#nvm\" class=\"hash-link\" aria-label=\"Direct link to NVM\" title=\"Direct link to NVM\" translate=\"no\">​</a></h2>\n<p>Docker Dev comes preconfigured with NVM to manage your Node versions. To avoid taking up extra space unnecessarily, it <strong>uses the downloaded Node versions already present on your host machine</strong>. (See in <code>docker-compose.yml</code>, the line <code>- ~/.nvm:/home/${USER}/.nvm</code> that mounts your host's <code>~/.nvm</code> folder inside the container.)</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"advanced-features\">Advanced features<a href=\"https://zwyx.dev/blog/docker-dev#advanced-features\" class=\"hash-link\" aria-label=\"Direct link to Advanced features\" title=\"Direct link to Advanced features\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"integration-with-other-applications\">Integration with other applications<a href=\"https://zwyx.dev/blog/docker-dev#integration-with-other-applications\" class=\"hash-link\" aria-label=\"Direct link to Integration with other applications\" title=\"Direct link to Integration with other applications\" translate=\"no\">​</a></h3>\n<p>Docker Dev provides useful scripts to integrate it with other applications (Git clients, etc.).</p>\n<p>Use the script <code>docker-dev/misc/open-in-vscode.sh</code> to configure the external editor of any application that supports it. For instance with <a href=\"https://www.sublimemerge.com/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Sublime Merge</a>:</p>\n<div class=\"imageWrapper_u91s withLegend_q8O0\"><div style=\"border-radius:5px\" class=\"frame_zT4L\"><img style=\"border-radius:5px\" class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/sublimeMerge-4e4367d9755e0eef123cbd2f5163a0ae.webp\" alt=\"Sublime Merge external editor configuration.\"></div><div class=\"legend_xj0V\">Sublime Merge external editor configuration</div></div>\n<p><code>open-in-vscode.sh</code> <strong>resolves the closest Git repository</strong> from the path passed as argument (or the current working directory if no argument is provided), and opens it in <strong>either a VS Code window attached to the dev container, or a regular one if the directory contains a file named <code>.nocontainer</code></strong>. (If a window on this project is already open, VS Code will simply focus it.)</p>\n<p>Another script – <code>docker-dev/misc/open-in-terminal.sh</code> – opens the path passed as argument in either a terminal session inside the dev container, or a regular one if the directory contains a file named <code>.nocontainer</code>.</p>\n<p>If you need to create <code>.nocontainer</code> files, but you'd prefer not to commit them or add them to every project's <code>.gitignore</code>, you can simply add <code>.nocontainer</code> to your global ignore file:</p>\n<div class=\"language-ignore codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">~/.config/git/ignore</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ignore codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">.nocontainer</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"host-command-execution\">Host command execution<a href=\"https://zwyx.dev/blog/docker-dev#host-command-execution\" class=\"hash-link\" aria-label=\"Direct link to Host command execution\" title=\"Direct link to Host command execution\" translate=\"no\">​</a></h3>\n<p>The scripts in the previous section allow you to open VS Code inside the dev container from another application. But how to do the opposite: open an external application from inside the dev container?</p>\n<p>The <strong>Host command execution</strong> feature of Docker Dev addresses exactly this: it allows to <strong>execute commands on the host machine from within the container</strong>.</p>\n<p>It uses a messaging system with a service that watches for command requests from the container and executes them on the host.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"setup-1\">Setup<a href=\"https://zwyx.dev/blog/docker-dev#setup-1\" class=\"hash-link\" aria-label=\"Direct link to Setup\" title=\"Direct link to Setup\" translate=\"no\">​</a></h4>\n<ol>\n<li class=\"\">Install <code>inotify-tools</code>:</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">sudo</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">apt</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">install</span><span class=\"token plain\"> inotify-tools</span><br></span></code></pre></div></div>\n<ol start=\"2\">\n<li class=\"\">Make sure the watcher script is executable:</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">chmod</span><span class=\"token plain\"> +x host-exec-service/host-exec.sh</span><br></span></code></pre></div></div>\n<ol start=\"3\">\n<li class=\"\">Install and start the systemd service – replace <code>&lt;path-to-docker-dev&gt;</code> below:</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">mkdir</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-p</span><span class=\"token plain\"> ~/.config/systemd/user</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">ln</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-s</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;path-to-docker-dev&gt;/host-exec-service/host-exec.service\"</span><span class=\"token plain\"> ~/.config/systemd/user/</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">systemctl </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--user</span><span class=\"token plain\"> daemon-reload</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">systemctl </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--user</span><span class=\"token plain\"> </span><span class=\"token builtin class-name\">enable</span><span class=\"token plain\"> host-exec.service</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">systemctl </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--user</span><span class=\"token plain\"> start host-exec.service</span><br></span></code></pre></div></div>\n<ol start=\"4\">\n<li class=\"\">Recreate the container, for the volume <code>~/.docker-dev</code> to be mounted:</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">docker-compose</span><span class=\"token plain\"> up </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--build</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-d</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"make-your-own-commands\">Make your own commands<a href=\"https://zwyx.dev/blog/docker-dev#make-your-own-commands\" class=\"hash-link\" aria-label=\"Direct link to Make your own commands\" title=\"Direct link to Make your own commands\" translate=\"no\">​</a></h4>\n<p>Create <code>host-exec-xxx</code> files specific to your use case (like the ones in <code>docker-dev/container-content</code>) and adapt the file <code>docker-dev/host-exec-service/host-exec.sh</code> (mainly the array <code>ALLOWED_COMMANDS</code> – restart the service after modifying this file: <code>systemctl --user restart host-exec.service</code>).</p>\n<p>For instance, the file <code>host-exec-sublime-merge</code> allows to open Sublime Merge from a VS Code window attached to the container. It uses the extension <a href=\"https://marketplace.visualstudio.com/items?itemName=adhamu.history-in-sublime-merge\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">History in Sublime Merge</a>, with its setting <code>history-in-sublime-merge.path</code> in the container set to <code>host-exec-sublime-merge</code>. It also requires a script <code>open-in-sublime-merge.sh</code> (see <code>host-exec.sh</code>).</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"security-aspects-to-keep-in-mind\">Security aspects to keep in mind<a href=\"https://zwyx.dev/blog/docker-dev#security-aspects-to-keep-in-mind\" class=\"hash-link\" aria-label=\"Direct link to Security aspects to keep in mind\" title=\"Direct link to Security aspects to keep in mind\" translate=\"no\">​</a></h4>\n<p>Make sure the host command execution service maintains the following features in order to prevent malicious scripts from escaping the container:</p>\n<ul>\n<li class=\"\"><strong>Whitelist-based</strong>: only approved commands can be executed.</li>\n<li class=\"\"><strong>Path validation</strong>: commands can only target directories under your main workspace folder.</li>\n<li class=\"\"><strong>Arguments validation</strong>: arguments are only allowed for specific, safe commands.</li>\n<li class=\"\"><strong>Shell injection prevention</strong>: potentially dangerous characters are blocked.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"final-thoughts\">Final thoughts<a href=\"https://zwyx.dev/blog/docker-dev#final-thoughts\" class=\"hash-link\" aria-label=\"Direct link to Final thoughts\" title=\"Direct link to Final thoughts\" translate=\"no\">​</a></h2>\n<p>Setting up this system takes 20-30 minutes. In exchange, you get:</p>\n<ul>\n<li class=\"\"><strong>Protection</strong> from malicious NPM packages that could compromise your system.</li>\n<li class=\"\"><strong>Peace of mind</strong> when installing dependencies from unfamiliar packages.</li>\n<li class=\"\"><strong>Instant access</strong> to a consistent development environment.</li>\n<li class=\"\"><strong>Flexibility</strong> to opt out specific projects with a simple <code>.nocontainer</code> file.</li>\n</ul>\n<p>For anyone who frequently works with Node.js projects, especially those involving third-party packages or untrusted code, this setup provides a significant security improvement with minimal overhead.</p>\n<p>Give it a try at <a href=\"https://github.com/zwyx/docker-dev\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">github.com/zwyx/docker-dev</a>.</p>",
            "url": "https://zwyx.dev/blog/docker-dev",
            "title": "Protect yourself from malicious NPM packages with a system-wide dev container",
            "summary": "Isolate Node.js development from your host system using a persistent Docker container.",
            "date_modified": "2025-11-27T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "docker",
                "security",
                "npm",
                "node",
                "vscode"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/base-conversions-with-big-numbers-in-javascript",
            "content_html": "<p>For the <a href=\"https://babel.zwyx.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Library of Babel</a>, I needed to convert very big numbers — hundreds of thousands of digits — from a base to another.</p>\n<!-- -->\n<p>However, <code>BigInt</code> is limited:</p>\n<ul>\n<li class=\"\">from string → the constructor <code>BigInt()</code> accepts strings in bases 2, 8, 10, and 16, only;</li>\n<li class=\"\">to string → <code>toString()</code> accepts a base of between 2 and 36.</li>\n</ul>\n<p>I needed to work with bases 29 and 94, <code>BigInt</code> wasn't able to handle this. So I tried to implement the necessary algorithms in JavaScript.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"from-string\">From string<a href=\"https://zwyx.dev/blog/base-conversions-with-big-numbers-in-javascript#from-string\" class=\"hash-link\" aria-label=\"Direct link to From string\" title=\"Direct link to From string\" translate=\"no\">​</a></h2>\n<p>To convert a <strong>string to an integer</strong>, the classic algorithm is:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> fromBase </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">text</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> alphabet</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"0123456789\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> bigint </span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> base </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">BigInt</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0n</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Start from the left of the string</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">for</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> i </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"> i </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\"> text</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"> i</span><span class=\"token operator\" style=\"color:#393A34\">++</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// for each digit, take the previous result,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// multiply it by the base, and add the new digit</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tresult </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:#393A34\">*</span><span class=\"token plain\"> base </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">BigInt</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">indexOf</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">text</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">charAt</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">i</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">/* or we could do that, but it's slower:</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t// starting from the right of the string</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\tfor (let i = text.length - 1; i &gt;= 0; i--) {</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t\t// multiply each digit by the base to the power of</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t\t// the digit position, and add it to the result</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t\tresult +=</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t\t\tBigInt(alphabet.indexOf(text.charAt(i))) *</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t\t\tbase ** BigInt(text.length - 1 - i);</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t}</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">\t*/</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>However, this is quite slow: it takes <strong>30s</strong> on my machine to convert a string containing <strong>500,000 random digits in base 10</strong>. (I tried to compile it to WebAssembly with AssemblyScript, but it made it even slower.)</p>\n<p>Whereas it takes V8's <code>BigInt</code> only <strong>100ms</strong>!</p>\n<p>Turns out, <code>BigInt</code> uses a different algorithm, which can be found in V8's <a href=\"https://github.com/v8/v8/blob/19853e10095df4aa640d6d3377c532c8a22ae1c5/src/bigint/fromstring.cc#L50\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>FromStringLarge</code></a> function.</p>\n<p>I'll leave it to you to read the description in V8's source code to understand how it works, and here is my JavaScript version:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> fromBase </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">text</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> alphabet</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"0123456789\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> bigint </span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> base </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">BigInt</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> parts </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> text</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">split</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">map</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">part</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token function\" style=\"color:#d73a49\">BigInt</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">indexOf</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">part</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> base</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">parts</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> pairFull</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">boolean</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">while</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">parts</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length </span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">2</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tpairFull </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">false</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tparts </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token generic-function function\" style=\"color:#d73a49\">reduce</span><span class=\"token generic-function generic class-name operator\" style=\"color:#393A34\">&lt;</span><span class=\"token generic-function generic class-name\">bigint</span><span class=\"token generic-function generic class-name punctuation\" style=\"color:#393A34\">[</span><span class=\"token generic-function generic class-name punctuation\" style=\"color:#393A34\">]</span><span class=\"token generic-function generic class-name punctuation\" style=\"color:#393A34\">[</span><span class=\"token generic-function generic class-name punctuation\" style=\"color:#393A34\">]</span><span class=\"token generic-function generic class-name operator\" style=\"color:#393A34\">&gt;</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">acc</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> cur</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> i</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token operator\" style=\"color:#393A34\">!</span><span class=\"token plain\">pairFull</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">i </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\tacc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">push</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">cur</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">else</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\tacc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">push</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t\tcur</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">*</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">i </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">i </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t\tcur</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">*</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">i </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\tpairFull </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">else</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\tpairFull </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">false</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> acc</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">*</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> parts</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>It now takes <strong>120ms</strong> to do the conversion! Just slightly slower than native <code>BigInt</code>. (And without any optimisations that are mentioned in V8's code.)</p>\n<p>It's a bit counter intuitive, because this algorithm does about 1,000,000 iterations, instead of 500,000 for the classic algorithm (which does one for each digit). The reason is apparently that the multiplications are smaller, and possibly more optimisable because involving parts of the same size.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"to-string\">To string<a href=\"https://zwyx.dev/blog/base-conversions-with-big-numbers-in-javascript#to-string\" class=\"hash-link\" aria-label=\"Direct link to To string\" title=\"Direct link to To string\" translate=\"no\">​</a></h2>\n<p>To convert an <strong>integer to a string</strong>, the classic algorithm is the successive division method:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> toBase </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">value</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> bigint</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> alphabet</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"0123456789\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> base </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">BigInt</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// At start, the dividend is equal to the value</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> dividend </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> value</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> remainder</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">while</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">dividend </span><span class=\"token operator\" style=\"color:#393A34\">&gt;=</span><span class=\"token plain\"> base</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// We get the remainder of the dividend modulo the base</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tremainder </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> dividend </span><span class=\"token operator\" style=\"color:#393A34\">%</span><span class=\"token plain\"> base</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// and we append it to the result, from right to left</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tresult </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token function\" style=\"color:#d73a49\">Number</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">remainder</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// then we divide the dividend by the base</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// and that gives us the dividend for the next iteration</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tdividend </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> dividend </span><span class=\"token operator\" style=\"color:#393A34\">/</span><span class=\"token plain\"> base</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tresult </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token function\" style=\"color:#d73a49\">Number</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">dividend</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>However, this is again quite slow: 30s to convert a bigint of 500,000 random digits in base 10.</p>\n<p>For this too, V8's <code>BigInt</code> is much faster, and uses a different algorithm, <a href=\"https://github.com/v8/v8/blob/19853e10095df4aa640d6d3377c532c8a22ae1c5/src/bigint/tostring.cc#L275\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><em>divide-and-conquer conversion</em></a>.</p>\n<p>I recommend again to read the description in V8's code, as I couldn't explain it better, and here is my JavaScript version:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> toBase </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">value</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> bigint</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> alphabet</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"0123456789\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> base </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">BigInt</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">alphabet</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">let</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> divisors </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">base</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> numberOfBitsInValue </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> value</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">toString</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">2</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">while</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">divisors</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">at</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">toString</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">2</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length </span><span class=\"token operator\" style=\"color:#393A34\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">2</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">&lt;=</span><span class=\"token plain\"> numberOfBitsInValue</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tdivisors</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">push</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">divisors</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">at</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">**</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">2n</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token function-variable function\" style=\"color:#d73a49\">divide</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">dividend</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> bigint</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> divisorIndex</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">number</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> divisor </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> divisors</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">divisorIndex</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> remainder </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> dividend </span><span class=\"token operator\" style=\"color:#393A34\">%</span><span class=\"token plain\"> divisor</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> newDividend </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> dividend </span><span class=\"token operator\" style=\"color:#393A34\">/</span><span class=\"token plain\"> divisor</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">divisorIndex </span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token function\" style=\"color:#d73a49\">divide</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">remainder</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> divisorIndex </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token function\" style=\"color:#d73a49\">divide</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">newDividend</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> divisorIndex </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">else</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\tresult </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">alphabet</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">[</span><span class=\"token template-string interpolation function\" style=\"color:#d73a49\">Number</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string interpolation\">newDividend</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">)</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">]</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token template-string interpolation\">\t\t\t\talphabet</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">[</span><span class=\"token template-string interpolation function\" style=\"color:#d73a49\">Number</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string interpolation\">remainder</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">)</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">]</span><span class=\"token template-string interpolation\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token template-string interpolation\">\t\t\t</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">result</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">divide</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">value</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> divisors</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token plain\">length </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tresult </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">replace</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token keyword\" style=\"color:#00009f\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\">RegExp</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">^</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">alphabet</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">[</span><span class=\"token template-string interpolation number\" style=\"color:#36acaa\">0</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">]</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string string\" style=\"color:#e3116c\">*</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>It now takes <strong>150ms</strong> to do the conversion! (Also without any optimisations.)</p>\n<hr>\n<p>And that's it. These two fast algorithms made possible my version of the <a href=\"https://babel.zwyx.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">the Library of Babel</a>, which works entirely client-side.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"unexpected-turn-of-events\">Unexpected turn of events!<a href=\"https://zwyx.dev/blog/base-conversions-with-big-numbers-in-javascript#unexpected-turn-of-events\" class=\"hash-link\" aria-label=\"Direct link to Unexpected turn of events!\" title=\"Direct link to Unexpected turn of events!\" translate=\"no\">​</a></h2>\n<p>Thanks to these algorithms, I was able to finish the <a href=\"https://babel.zwyx.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">the Library of Babel</a>. Every operation was taking a couple of seconds, at most, on my computer.</p>\n<p>However, when testing the app on my phone, I realised that one operation was very slow: it took more than 3 minutes!</p>\n<p>It turned out that it was a <code>.toString()</code> call on a very large bigint. The need was to convert this bigint to a string in base 10, so I didn't bother changing this <code>.toString()</code> to use the new algorithm <code>toBase</code>, because <code>.toString()</code> is able to handle base 10.</p>\n<p>However, V8's <a href=\"https://github.com/v8/v8/blob/main/src/bigint/tostring.cc\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">source code</a> shows that the fast <code>.toString</code> algorithm is behind the flag <code>V8_ADVANCED_BIGINT_ALGORITHMS</code>.</p>\n<p>I guess this flag is ON when Chrome is built for Linux desktop, but OFF when it's built for Android.</p>\n<p>I simply replaced <code>.toString()</code> by a call to <code>toBase(...)</code> and the app became fast on my phone too!</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>There is also a call to <code>.toString(16)</code> on the same number, but this one is very quick. The algorithm for base 16 must be much faster because it just has to split every byte into two hexadecimal values. So I didn't have to change this one.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"future\">Future<a href=\"https://zwyx.dev/blog/base-conversions-with-big-numbers-in-javascript#future\" class=\"hash-link\" aria-label=\"Direct link to Future\" title=\"Direct link to Future\" translate=\"no\">​</a></h2>\n<p>There is <a href=\"https://github.com/tc39/proposal-number-fromstring\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">a TC39 proposal</a> to give <code>BigInt</code> more capabilities, but as of 2023, it doesn't look like it's getting much traction.</p>",
            "url": "https://zwyx.dev/blog/base-conversions-with-big-numbers-in-javascript",
            "title": "Base conversions with BIG numbers in JavaScript",
            "summary": "How to rapidly convert very big numbers from one base to another in JavaScript.",
            "date_modified": "2024-02-06T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "base conversion",
                "v8",
                "integer",
                "bigint",
                "string",
                "algorithm",
                "javascript"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/typesafe-translations",
            "content_html": "<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"data:image/jpeg;base64,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JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJhYBJFJJJLpJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJc+rFaGIzEOPpJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIPbdR9Ug9eJNJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJONIH1hpXiU1JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLpVDNCEJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ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\" alt=\"Translations\"></div></div>\n<p>Use TypeScript to ensure the validity of your i18next translations.</p>\n<!-- -->\n<hr>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>A demo project is available at <a href=\"https://github.com/zwyx/typesafe-translations\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">github.com/zwyx/typesafe-translations</a></p></div></div>\n<p>Translating software with i18next is easy:</p>\n<ul>\n<li class=\"\">we replace hard-coded text, for instance <code>Hello</code>, by a function call with a key, <code>t(\"hello\")</code>,</li>\n<li class=\"\">we create translation files containing the text for the key in different languages:</li>\n</ul>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">en.json</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"hello\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"Hello\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">fr.json</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"hello\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"Bonjour\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<p>The problem is that there is <strong>no easy way of detecting issues in translations</strong>. For instance, if we change the key from <code>hello</code> to <code>hi</code>, then we have to update all the translation files manually.</p>\n<p>Many utilities have been made to improve the situation, often requirering extra build steps. Recently however, <strong>it has became very nice and easy with i18next and TypeScript</strong>.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>This example uses a classic React single-page application, with no Server-Side Rendering or Static Site Generation. If you plan to use SSR or SSG — for instance with Next.js — you will have a more complex setup to create, in order to prevent hydration errors.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"make-the-main-language-the-reference\">Make the main language the reference<a href=\"https://zwyx.dev/blog/typesafe-translations#make-the-main-language-the-reference\" class=\"hash-link\" aria-label=\"Direct link to Make the main language the reference\" title=\"Direct link to Make the main language the reference\" translate=\"no\">​</a></h2>\n<p>The types will be set by the translation file of our main language. For us, it will be English. It will become the source of truth.</p>\n<p>All other language files, as well as the code of the app, will be type checked against it.</p>\n<p>We start by creating the English translation file, as a TypeScript file:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">en.ts</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> en </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tapp</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\thello</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"Hello\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>Then, we want to create a type derived from this file, that we'll use with the other languages.</p>\n<p>To do this, we create a <code>DeepReplace</code> utility type. It creates an interface from an object, containing all the keys of this object, with all their types changed to <code>string</code>.</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">utils.ts</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// https://stackoverflow.com/questions/60437172/typescript-deep-replace-multiple-types</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">type</span><span class=\"token plain\"> </span><span class=\"token class-name\">Replacement</span><span class=\"token class-name operator\" style=\"color:#393A34\">&lt;</span><span class=\"token class-name constant\" style=\"color:#36acaa\">M</span><span class=\"token class-name\"> </span><span class=\"token class-name keyword\" style=\"color:#00009f\">extends</span><span class=\"token class-name\"> </span><span class=\"token class-name punctuation\" style=\"color:#393A34\">[</span><span class=\"token class-name builtin\">unknown</span><span class=\"token class-name punctuation\" style=\"color:#393A34\">,</span><span class=\"token class-name\"> </span><span class=\"token class-name builtin\">unknown</span><span class=\"token class-name punctuation\" style=\"color:#393A34\">]</span><span class=\"token class-name punctuation\" style=\"color:#393A34\">,</span><span class=\"token class-name\"> </span><span class=\"token class-name constant\" style=\"color:#36acaa\">T</span><span class=\"token class-name operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">M</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">extends</span><span class=\"token plain\"> </span><span class=\"token class-name builtin\">unknown</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">T</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">extends</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">M</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">M</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">never</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\">never</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">type</span><span class=\"token plain\"> </span><span class=\"token class-name\">DeepReplace</span><span class=\"token class-name operator\" style=\"color:#393A34\">&lt;</span><span class=\"token class-name constant\" style=\"color:#36acaa\">T</span><span class=\"token class-name punctuation\" style=\"color:#393A34\">,</span><span class=\"token class-name\"> </span><span class=\"token class-name constant\" style=\"color:#36acaa\">M</span><span class=\"token class-name\"> </span><span class=\"token class-name keyword\" style=\"color:#00009f\">extends</span><span class=\"token class-name\"> </span><span class=\"token class-name punctuation\" style=\"color:#393A34\">[</span><span class=\"token class-name builtin\">unknown</span><span class=\"token class-name punctuation\" style=\"color:#393A34\">,</span><span class=\"token class-name\"> </span><span class=\"token class-name builtin\">unknown</span><span class=\"token class-name punctuation\" style=\"color:#393A34\">]</span><span class=\"token class-name operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">P</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">in</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">keyof</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">T</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">T</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">P</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">extends</span><span class=\"token plain\"> </span><span class=\"token class-name constant\" style=\"color:#36acaa\">M</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> Replacement</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token constant\" style=\"color:#36acaa\">M</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">T</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">P</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">T</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">P</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">extends</span><span class=\"token plain\"> </span><span class=\"token class-name\">object</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t  </span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> DeepReplace</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token constant\" style=\"color:#36acaa\">T</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">P</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">M</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t  </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">T</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token constant\" style=\"color:#36acaa\">P</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>To create our <code>I18nLocale</code> type, we use <code>DeepReplace</code> with the <code>en</code> object. The English translation file becomes:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">en.ts</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"> DeepReplace </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"~/lib/utils\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> en </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tapp</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\thello</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"Hello\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">type</span><span class=\"token plain\"> </span><span class=\"token class-name\">I18nLocale</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> DeepReplace</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> en</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token builtin\">string</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token builtin\">string</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"make-the-other-languages-follow-the-types-of-the-main-one\">Make the other languages follow the types of the main one<a href=\"https://zwyx.dev/blog/typesafe-translations#make-the-other-languages-follow-the-types-of-the-main-one\" class=\"hash-link\" aria-label=\"Direct link to Make the other languages follow the types of the main one\" title=\"Direct link to Make the other languages follow the types of the main one\" translate=\"no\">​</a></h2>\n<p>We want to use our <code>I18nLocale</code> in all other translation files:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">fr.ts</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"> I18nLocale </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"./en\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> fr</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> I18nLocale </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tapp</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\thello</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"Bonjour\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>Our translation files are now typed. A typo, or a missing key, is detected immediately:</p>\n<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"data:image/jpeg;base64,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\" alt=\"Typo in translation file\"></div></div>\n<p>We also now have intellisense available in the translation files:</p>\n<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"data:image/jpeg;base64,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\" alt=\"Intellisense in translation file\"></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"use-the-types-in-the-code\">Use the types in the code<a href=\"https://zwyx.dev/blog/typesafe-translations#use-the-types-in-the-code\" class=\"hash-link\" aria-label=\"Direct link to Use the types in the code\" title=\"Direct link to Use the types in the code\" translate=\"no\">​</a></h2>\n<p>In order to let i18next know about our types, we first create a <code>resources</code> constant containing all our languages, in the i18next initialisation file:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">i18n.ts</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"> en </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"./locales/en\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"> fr </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"./locales/fr\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> resources </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\ten</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tfr</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>We then create the file <code>src/@types/i18next.d.ts</code> – the path is important – containing:</p>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">src/@types/i18next.d.ts</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"> resources </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"@/i18n/i18n\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">declare</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">module</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"i18next\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">interface</span><span class=\"token plain\"> </span><span class=\"token class-name\">CustomTypeOptions</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tresources</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> resources</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token string\" style=\"color:#e3116c\">\"en\"</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<p>And that's it! Now our types are checked in the code:</p>\n<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"data:image/jpeg;base64,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\" alt=\"Intellisense in translation file\"></div></div>\n<p>And we have Intellisense:</p>\n<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/5-b6971f94911e1d8a1ce2223070007c2b.jpg\" alt=\"Intellisense in translation file\"></div></div>\n<p>For more details on the implementation, check out the demo project at <a href=\"https://github.com/zwyx/typesafe-translations\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">github.com/zwyx/typesafe-translations</a>.</p>",
            "url": "https://zwyx.dev/blog/typesafe-translations",
            "title": "Type-safe translations with TypeScript and i18next",
            "summary": "Use TypeScript to ensure the validity of your i18next translations.",
            "date_modified": "2023-09-19T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "i18n",
                "i18next",
                "vite",
                "typescript",
                "react",
                "tailwind",
                "shadcnui"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/proper-dark-theme",
            "content_html": "<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"data:image/webp;base64,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\" alt=\"Translations\"></div></div>\n<p>Three states, no flash, reactive. Test it at <a href=\"https://zwyx.github.io/proper-dark-theme\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">zwyx.github.io/proper-dark-theme</a></p>\n<!-- -->\n<hr>\n<p>Although implementing dark theme manually might sound like reinventing the wheel, it's actually easy and there are good reasons to do it:</p>\n<ul>\n<li class=\"\">dark theme libraries depend on a specific stack,</li>\n<li class=\"\">UI/component libraries often — <em>incredibly often</em>, based on the dozen I've tested — don't correctly implement the following requirements.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"three-requirements-for-a-proper-dark-theme\">Three requirements for a proper dark theme<a href=\"https://zwyx.dev/blog/proper-dark-theme#three-requirements-for-a-proper-dark-theme\" class=\"hash-link\" aria-label=\"Direct link to Three requirements for a proper dark theme\" title=\"Direct link to Three requirements for a proper dark theme\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"three-states\">Three states<a href=\"https://zwyx.dev/blog/proper-dark-theme#three-states\" class=\"hash-link\" aria-label=\"Direct link to Three states\" title=\"Direct link to Three states\" translate=\"no\">​</a></h3>\n<p>The user must be able to choose <code>same as device</code> (which should be the default), <code>light</code>, or <code>dark</code>.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"no-flash\">No flash<a href=\"https://zwyx.dev/blog/proper-dark-theme#no-flash\" class=\"hash-link\" aria-label=\"Direct link to No flash\" title=\"Direct link to No flash\" translate=\"no\">​</a></h3>\n<p>If the current theme is dark, the page must not display a white background while it loads (very annoying at night, and possibly problematic for users with vision disabilities).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"reactive\">Reactive<a href=\"https://zwyx.dev/blog/proper-dark-theme#reactive\" class=\"hash-link\" aria-label=\"Direct link to Reactive\" title=\"Direct link to Reactive\" translate=\"no\">​</a></h3>\n<p>Changing the theme — including the device's theme, when <code>same as device</code> is selected — should be instantly effective on the page, without requiring to refresh it.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>In order to have these features, and not being dependent on a specific stack, I found that the best it to implement dark theme manually.</p><p>Once we know how to do it, we can reuse it everywhere. Instead of installing it as a library, we copy and paste it. We « own the code », which is a philosophy I first discovered with <a href=\"https://ui.shadcn.com/docs\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Shadcn UI</a>.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"implementation\">Implementation<a href=\"https://zwyx.dev/blog/proper-dark-theme#implementation\" class=\"hash-link\" aria-label=\"Direct link to Implementation\" title=\"Direct link to Implementation\" translate=\"no\">​</a></h2>\n<p>Our way of doing it will be:</p>\n<ul>\n<li class=\"\">have JavaScript applying a class <code>light</code> or <code>dark</code> to the root HTML element, and</li>\n<li class=\"\">have different CSS variables depending on this class.</li>\n</ul>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>A demonstration of this implementation is deployed <a href=\"https://zwyx.github.io/proper-dark-theme/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>, and its code is present <a href=\"https://github.com/zwyx/proper-dark-theme\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-css\">The CSS<a href=\"https://zwyx.dev/blog/proper-dark-theme#the-css\" class=\"hash-link\" aria-label=\"Direct link to The CSS\" title=\"Direct link to The CSS\" translate=\"no\">​</a></h3>\n<p>CSS variables make it easy to have our app reactive: changing the class on the root HTML element instantly change all the color in the website, no refresh necessary.</p>\n<div class=\"language-css codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token selector pseudo-class\" style=\"color:#00009f\">:root</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">/* variables for light theme */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--background</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">100</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--foreground</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">222.2</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">84</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">4.9</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token selector pseudo-class\" style=\"color:#00009f\">:root</span><span class=\"token selector class\" style=\"color:#00009f\">.dark</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">/* variables for dark theme */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--background</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">222.2</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">84</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">4.9</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--foreground</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">210</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">40</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">98</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-html\">The HTML<a href=\"https://zwyx.dev/blog/proper-dark-theme#the-html\" class=\"hash-link\" aria-label=\"Direct link to The HTML\" title=\"Direct link to The HTML\" translate=\"no\">​</a></h3>\n<p>To prevent the flash, we want to apply the <code>light</code> or <code>dark</code> class as soon as possible. This is what this small block of JavaScript, to be placed in the <code>head</code> of our HTML, does:</p>\n<div class=\"language-html codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-html codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">meta</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">name</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">theme-color</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">content</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">#020817</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript comment\" style=\"color:#999988;font-style:italic\">// Set the theme's class as soon as possible to prevent a flash of the wrong theme</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript keyword\" style=\"color:#00009f\">var</span><span class=\"token script language-javascript\"> lightThemeName </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"light\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript keyword\" style=\"color:#00009f\">var</span><span class=\"token script language-javascript\"> darkThemeName </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"dark\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript keyword\" style=\"color:#00009f\">var</span><span class=\"token script language-javascript\"> storedTheme </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript dom variable\" style=\"color:#36acaa\">localStorage</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript method function property-access\" style=\"color:#d73a49\">getItem</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"theme\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript keyword\" style=\"color:#00009f\">var</span><span class=\"token script language-javascript\"> theme</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript\">storedTheme </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">===</span><span class=\"token script language-javascript\"> lightThemeName </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">||</span><span class=\"token script language-javascript\"> storedTheme </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">===</span><span class=\"token script language-javascript\"> darkThemeName</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t\ttheme </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> storedTheme</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript keyword control-flow\" style=\"color:#00009f\">else</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript function\" style=\"color:#d73a49\">matchMedia</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"(prefers-color-scheme: dark)\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript property-access\">matches</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t\ttheme </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> darkThemeName</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript keyword control-flow\" style=\"color:#00009f\">else</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t\ttheme </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> lightThemeName</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript dom variable\" style=\"color:#36acaa\">document</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript property-access\">documentElement</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript property-access\">classList</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript method function property-access\" style=\"color:#d73a49\">add</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript\">theme</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript\">theme </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">===</span><span class=\"token script language-javascript\"> lightThemeName</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t\t</span><span class=\"token script language-javascript dom variable\" style=\"color:#36acaa\">document</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t\t\t</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript method function property-access\" style=\"color:#d73a49\">querySelector</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">'meta[name=\"theme-color\"]'</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t\t\t</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">?.</span><span class=\"token script language-javascript method function property-access\" style=\"color:#d73a49\">setAttribute</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"content\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"#ffffff\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">\t</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token script language-javascript\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><br></span></code></pre></div></div>\n<p>The <code>theme-color</code> meta tag is optional, it's useful mainly if our app is a PWA. When using it, we want to keep it in sync with our theme color.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-js\">The JS<a href=\"https://zwyx.dev/blog/proper-dark-theme#the-js\" class=\"hash-link\" aria-label=\"Direct link to The JS\" title=\"Direct link to The JS\" translate=\"no\">​</a></h3>\n<p>The JS has to maintain two state variables:</p>\n<ul>\n<li class=\"\">the user preference: <code>light</code>, <code>dark</code>, or <code>same as devices</code></li>\n<li class=\"\">the currently displayed theme:<!-- -->\n<ul>\n<li class=\"\"><code>light</code>, when:<!-- -->\n<ul>\n<li class=\"\">the user preference is <code>light</code></li>\n<li class=\"\">the user preference is <code>same as device</code> and the device's theme is light</li>\n</ul>\n</li>\n<li class=\"\"><code>dark</code>, when:<!-- -->\n<ul>\n<li class=\"\">the user preference is <code>dark</code></li>\n<li class=\"\">the user preference is <code>same as device</code> and the device's theme is dark</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n<p>To do that, the JS has to:</p>\n<ul>\n<li class=\"\">react to changes of the user's theme preference,</li>\n<li class=\"\">store it in local storage,</li>\n<li class=\"\">listen for changes of the system's theme, which is done by adding an event listener to a media query:</li>\n</ul>\n<div class=\"language-ts codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ts codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">matchMedia</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"(prefers-color-scheme: dark)\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token function\" style=\"color:#d73a49\">addEventListener</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"change\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">...</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">update the HTML root element's class according to the user preference and the system's theme.</li>\n</ul>\n<p>I won't include the rest of the code here, instead I invite you to have a look at the <a href=\"https://github.com/Zwyx/proper-dark-theme/blob/main/src/lib/ThemeContext.tsx\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">implementation made with TypeScript and React</a> for the demo project.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"sidenote\">Sidenote<a href=\"https://zwyx.dev/blog/proper-dark-theme#sidenote\" class=\"hash-link\" aria-label=\"Direct link to Sidenote\" title=\"Direct link to Sidenote\" translate=\"no\">​</a></h2>\n<p>I consider this three-state implementation to be the best at the moment, but I hope that it won't be necessary in the future.</p>\n<p>When all operating systems and all web browsers will implement dark theme seamlessly, then offering to the user the possibility to change the theme for a particular website might start to be seen as unnecessary. At least, the setting could be buried in a dialog box instead of being directly available on the top right of the page.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"bonus-with-less-javascript\">Bonus: with less JavaScript<a href=\"https://zwyx.dev/blog/proper-dark-theme#bonus-with-less-javascript\" class=\"hash-link\" aria-label=\"Direct link to Bonus: with less JavaScript\" title=\"Direct link to Bonus: with less JavaScript\" translate=\"no\">​</a></h2>\n<p>Here's another way of defining the CSS variables, which greatly reduces the amount of JS required. We use a media query to apply the dark theme when it's the system preference and the user hasn't selected the light theme.</p>\n<div class=\"language-css codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token selector pseudo-class\" style=\"color:#00009f\">:root</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">/* variables for light theme */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--background</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">100</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--foreground</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">222.2</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">84</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">4.9</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token selector pseudo-class\" style=\"color:#00009f\">:root</span><span class=\"token selector class\" style=\"color:#00009f\">.dark</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">/* variables for dark theme */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--background</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">222.2</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">84</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">4.9</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token variable\" style=\"color:#36acaa\">--foreground</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">210</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">40</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">98</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token atrule rule\" style=\"color:#00a4db\">@media</span><span class=\"token atrule\" style=\"color:#00a4db\"> </span><span class=\"token atrule keyword\" style=\"color:#00009f\">only</span><span class=\"token atrule\" style=\"color:#00a4db\"> screen </span><span class=\"token atrule keyword\" style=\"color:#00009f\">and</span><span class=\"token atrule\" style=\"color:#00a4db\"> </span><span class=\"token atrule punctuation\" style=\"color:#393A34\">(</span><span class=\"token atrule property\" style=\"color:#36acaa\">prefers-color-scheme</span><span class=\"token atrule punctuation\" style=\"color:#393A34\">:</span><span class=\"token atrule\" style=\"color:#00a4db\"> dark</span><span class=\"token atrule punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token selector pseudo-class\" style=\"color:#00009f\">:root</span><span class=\"token selector pseudo-class\" style=\"color:#00009f\">:not</span><span class=\"token selector punctuation\" style=\"color:#393A34\">(</span><span class=\"token selector class\" style=\"color:#00009f\">.light</span><span class=\"token selector punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">/* variables for dark theme */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token variable\" style=\"color:#36acaa\">--background</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">222.2</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">84</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">4.9</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token variable\" style=\"color:#36acaa\">--foreground</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">210</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">40</span><span class=\"token unit\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">98</span><span class=\"token unit\">%</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<p>By doing that, the JS doesn't have to listen for changes of the system's theme anymore.</p>\n<p>This is how I used to do it. However, it has a few drawbacks:</p>\n<ul>\n<li class=\"\">we have to duplicate the declaration of the dark theme's variables,</li>\n<li class=\"\">the app cannot know which theme is currently displayed when the user preference is <code>same as device</code> (Tailwind's <code>dark:</code> selector, for instance, doesn't work).</li>\n</ul>",
            "url": "https://zwyx.dev/blog/proper-dark-theme",
            "title": "All you need for a proper dark theme",
            "summary": "Three states, no flash, reactive.",
            "date_modified": "2023-09-12T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "dark theme",
                "vite",
                "typescript",
                "react",
                "tailwind",
                "shadcnui"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/your-dotfiles-in-a-git-repo",
            "content_html": "<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/gears-unsplash-xRDuEeG1TVI-845693b4f819dad5a2b8c416aef8f540.webp\" alt=\"Gears\"></div></div>\n<p>Track changes in your config files using a Git repository.</p>\n<!-- -->\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"dotfiles\">Dotfiles<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#dotfiles\" class=\"hash-link\" aria-label=\"Direct link to Dotfiles\" title=\"Direct link to Dotfiles\" translate=\"no\">​</a></h2>\n<p><em>Dotfiles</em> is the name given to the tiny text files containing the configuration for a piece of software. They're often placed in the your home directory and start with a dot to be hidable. We'll not limit ourselves to strictly dotfiles in this article though, what we'll do apply to any configuration file.</p>\n<p>Backing up these files and track changes to them is a good idea. It will be useful if you have to reinstall your system or set up a new machine, and, probably more often, you can restore your configuration if the corresponding software blows a gasket. It can happened with VS Code and its feature \"Settings Sync\" for instance: I was using VS Code on my machine and on <a href=\"https://github.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GitHub.dev</a>, and settings got messed up. Thanks to the fact that my VS Code's config is tracked in Git, I just had to discard the current changes to have my config back.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"using-git-to-track-them\">Using Git to track them<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#using-git-to-track-them\" class=\"hash-link\" aria-label=\"Direct link to Using Git to track them\" title=\"Direct link to Using Git to track them\" translate=\"no\">​</a></h2>\n<p>Git is the first choice to track changes in text files. However, all these config are in different places on the file system.</p>\n<p>So how to gather them in one Git repository? There are a few methods:</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"1-create-hard-links-or-symlinks\">1. Create hard links or symlinks<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#1-create-hard-links-or-symlinks\" class=\"hash-link\" aria-label=\"Direct link to 1. Create hard links or symlinks\" title=\"Direct link to 1. Create hard links or symlinks\" translate=\"no\">​</a></h3>\n<p>This method is a pain: creating all the links is cumbersome. Also, some software delete and recreate their configuration files instead of modifying them, which breaks the links without us noticing it.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"2-make-your-home-directory-the-root-of-your-git-config-repository-and-add-only-the-files-you-want-to-track\">2. Make your home directory the root of your Git config repository, and add only the files you want to track<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#2-make-your-home-directory-the-root-of-your-git-config-repository-and-add-only-the-files-you-want-to-track\" class=\"hash-link\" aria-label=\"Direct link to 2. Make your home directory the root of your Git config repository, and add only the files you want to track\" title=\"Direct link to 2. Make your home directory the root of your Git config repository, and add only the files you want to track\" translate=\"no\">​</a></h3>\n<p>This isn't recommended if you have other Git repositories anywhere in your home directory, as there would be a risk of interference.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"3-use-a-bare-git-repository-and-set-its-worktree-to-your-home-directory\">3. Use a bare Git repository and set its worktree to your home directory<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#3-use-a-bare-git-repository-and-set-its-worktree-to-your-home-directory\" class=\"hash-link\" aria-label=\"Direct link to 3. Use a bare Git repository and set its worktree to your home directory\" title=\"Direct link to 3. Use a bare Git repository and set its worktree to your home directory\" translate=\"no\">​</a></h3>\n<p>The idea is to create a bare repository — which is simply a Git repository without a work tree — and then set its worktree to your home directory.</p>\n<blockquote>\n<p><em>How is this different from method 2?</em></p>\n<p>Nothing shows that the home folder is the root of a repo. <code>git</code>, run in your home folder, will not see it as a repository, so has no risk of interfering with other repos present in your home folder.</p>\n</blockquote>\n<blockquote>\n<p><em>If we set the worktree of the bare repository... then it's not bare anymore!</em></p>\n<p>Indeed. This method is known as the \"bare repo method\", but it might just be in order to avoid confusion with method 2. We actually don't need a bare repo, we just need to change the location of a repo's worktree. Don't be confused by this, or worried that it's too complex.</p>\n</blockquote>\n<p>This article is about setting up this method.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>If you're not keen to set this up yourself, or would like to have advanced features, have a look at existing dotfile managers. There are <a href=\"https://dotfiles.github.io/utilities/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">plenty</a>, and <a href=\"https://www.chezmoi.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">chezmoi</a> seems to have become a reference in recent years.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"setup\">Setup<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#setup\" class=\"hash-link\" aria-label=\"Direct link to Setup\" title=\"Direct link to Setup\" translate=\"no\">​</a></h2>\n<div class=\"theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"></path></svg></span>caution</div><div class=\"admonitionContent_BuS1\"><p>Please read carefully and only execute commands that you fully understand. Wrong Git commands executed anywhere in your home directory could lead to a loss of data.</p></div></div>\n<p>Start by creating a bare repository:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> init </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--bare</span><span class=\"token plain\"> ~/my-config</span><br></span></code></pre></div></div>\n<p>The folder <code>~/my-config</code> now contains the config repository. Now we want to set its worktree to be the home directory (replace <code>&lt;your-name&gt;</code> in the block below):</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token builtin class-name\">cd</span><span class=\"token plain\"> ~/my-config</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> config </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--unset</span><span class=\"token plain\"> core.bare</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> config core.worktree </span><span class=\"token string\" style=\"color:#e3116c\">'/home/&lt;your-name&gt;'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> config status.showUntrackedFiles no</span><br></span></code></pre></div></div>\n<p>The file <code>~/my-config/config</code> should look like this:</p>\n<div class=\"language-ini codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ini codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">[core]</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\trepositoryformatversion = 0</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tfilemode = true</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tworktree = /home/&lt;your-name&gt;</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">[status]</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tshowUntrackedFiles = no</span><br></span></code></pre></div></div>\n<p>Let's add two functions in your <code>.zshrc</code> (or equivalent) to easily add and untrack config files:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># We use functions instead of aliases to have folder and file name completion</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function-name function\" style=\"color:#d73a49\">config-add</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> --git-dir</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string environment constant\" style=\"color:#36acaa\">$HOME</span><span class=\"token string\" style=\"color:#e3116c\">/repo/config/.git-bare\"</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-f</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:#36acaa\">$@</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function-name function\" style=\"color:#d73a49\">config-untrack</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> --git-dir</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string environment constant\" style=\"color:#36acaa\">$HOME</span><span class=\"token string\" style=\"color:#e3116c\">/repo/config/.git-bare\"</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">rm</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--cached</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:#36acaa\">$@</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"usage\">Usage<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#usage\" class=\"hash-link\" aria-label=\"Direct link to Usage\" title=\"Direct link to Usage\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"adding-files-to-the-config\">Adding files to the config<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#adding-files-to-the-config\" class=\"hash-link\" aria-label=\"Direct link to Adding files to the config\" title=\"Direct link to Adding files to the config\" translate=\"no\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">config-add .zshrc</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"untrack-files-from-the-config\">Untrack files from the config<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#untrack-files-from-the-config\" class=\"hash-link\" aria-label=\"Direct link to Untrack files from the config\" title=\"Direct link to Untrack files from the config\" translate=\"no\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">config-untrack .zshrc</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"commit-push-etc\">Commit, push, etc.<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#commit-push-etc\" class=\"hash-link\" aria-label=\"Direct link to Commit, push, etc.\" title=\"Direct link to Commit, push, etc.\" translate=\"no\">​</a></h3>\n<div class=\"theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z\"></path></svg></span>danger</div><div class=\"admonitionContent_BuS1\"><p>Execute regular git commands in your config repository — <code>git add</code>, <code>git commit</code>, <code>git push</code>, etc. <strong>HOWEVER</strong>, be very careful: commands like <code>git clean</code> could delete every untracked files from the home directory!</p></div></div>\n<p>That's it. Your config files are tracked in a git repo. Keep reading for a few more tips.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"set-up-a-cron-task\">Set up a Cron task<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#set-up-a-cron-task\" class=\"hash-link\" aria-label=\"Direct link to Set up a Cron task\" title=\"Direct link to Set up a Cron task\" translate=\"no\">​</a></h3>\n<p>You can create a cron job that commits and pushes your config once a week:</p>\n<ul>\n<li class=\"\">create a script containing:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token builtin class-name\">cd</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string environment constant\" style=\"color:#36acaa\">$HOME</span><span class=\"token string\" style=\"color:#e3116c\">/my-config\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-A</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> commit </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-a</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-m</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"Weekly commit\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> push</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">run:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">crontab</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-e</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">and append:</li>\n</ul>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">30 12 * * 5 &lt;path-to-script&gt;</span><br></span></code></pre></div></div>\n<p>You can also add other useful tasks in the script, just before the the three <code>git</code> commands. For instance:</p>\n<ul>\n<li class=\"\"><code>code --list-extensions &gt; ~/.config/vscode-extensions.txt</code></li>\n<li class=\"\"><code>dconf dump / &gt; ~/.config/dconf.ini</code></li>\n<li class=\"\"><code>crontab -l &gt; ~/.config/crontab.txt</code></li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"special-cases\">Special cases<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#special-cases\" class=\"hash-link\" aria-label=\"Direct link to Special cases\" title=\"Direct link to Special cases\" translate=\"no\">​</a></h2>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"tracking-files-from-other-git-repositories\">Tracking files from other Git repositories<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#tracking-files-from-other-git-repositories\" class=\"hash-link\" aria-label=\"Direct link to Tracking files from other Git repositories\" title=\"Direct link to Tracking files from other Git repositories\" translate=\"no\">​</a></h3>\n<p>Adding files that are already present in another Git repository seems to be impossible.</p>\n<p>For instance, I didn't find a way to add <code>~/.nvm/default-packages</code>, because <code>~/.nvm</code> is a Git repository.</p>\n<p>So for this kind of file, I use the hard link method:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">ln</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:#36acaa\">`</span><span class=\"token variable\" style=\"color:#36acaa\">~/.nvm/default-packages</span><span class=\"token variable\" style=\"color:#36acaa\">`</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:#36acaa\">`</span><span class=\"token variable\" style=\"color:#36acaa\">~/my-config/default-packages</span><span class=\"token variable\" style=\"color:#36acaa\">`</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"sublime-merge-users\">Sublime Merge users<a href=\"https://zwyx.dev/blog/your-dotfiles-in-a-git-repo#sublime-merge-users\" class=\"hash-link\" aria-label=\"Direct link to Sublime Merge users\" title=\"Direct link to Sublime Merge users\" translate=\"no\">​</a></h3>\n<p>At the moment, Sublime Merge <a href=\"https://github.com/sublimehq/sublime_merge/issues/1544\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">ignores the property <code>status.showUntrackedFiles</code></a> and will show all the untracked files of the home folder.</p>\n<p>To prevent this, it is possible to add a <code>.gitignore</code> in the repo's root folder. This <code>.gitignore</code> mainly needs to contain a <code>*</code>, to hide all untracked files, although you can also \"unhide\" specific subfolders if you want your weekly commit to automatically includes new files from these folders.</p>\n<p>Here's an example:</p>\n<div class=\"language-txt codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-txt codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm\" style=\"counter-reset:line-count 0\"><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">/*</span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\" style=\"display:inline-block\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">!.gitignore</span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\" style=\"display:inline-block\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">!.config/Code</span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">.config/Code/*</span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">!.config/Code/User</span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">.config/Code/User/*</span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#393A34\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">!.config/Code/User/snippets</span></span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">line 1: hide everything;</li>\n<li class=\"\">line 3: unhide the file <code>.gitignore</code>;</li>\n<li class=\"\">line 5: unhide the folder <code>.config/Code</code>...</li>\n<li class=\"\">line 6: but hide everything inside this folder;</li>\n<li class=\"\">line 7: unhide the folder <code>.config/Code/User</code>...</li>\n<li class=\"\">line 8: but hide everything inside this folder;</li>\n<li class=\"\">line 9: unhide the folder <code>.config/Code/User/snippets</code>, so all files in this folder will be tracked in our config.</li>\n</ul>\n<div class=\"theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"></path></svg></span>caution</div><div class=\"admonitionContent_BuS1\"><p>This <code>.gitignore</code> file has a side effect on <code>git checkout</code>. Indeed, usually, when cloning a repository and running <code>git checkout</code>, if some files in the worktree are different than the ones in the repository, git displays the message <code>error: The following untracked working tree files would be overwritten by checkout: ...</code>. But with this <code>.gitignore</code> in the home directory, <code>git checkout</code> will simply override the files that are in the home directory by the ones from the repository. Without warning!</p><p>However, the point it to be able to use Sublime Merge, so we don't need <code>git checkout</code>. We simply open the repository in Sublime Merge, keep the files we want, and we discard the others.</p></div></div>\n<p>Sublime Merge has another bug when working with a repository whose work tree isn't next to the repo itself: <a href=\"https://github.com/sublimehq/sublime_merge/issues/1670\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">the corresponding tab isn't reopen correctly</a>. So, here's an alias to quickly open your config repo in Sublime Merge:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token builtin class-name\">alias</span><span class=\"token plain\"> config-open-in-sublime-merge</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token string\" style=\"color:#e3116c\">'\"$HOME/.config/open-in-sublime-merge.sh\" \"$HOME/my-config\"'</span><br></span></code></pre></div></div>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">open-in-sublime-merge.sh</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token shebang important\">#!/bin/bash</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> pgrep sublime_merge</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">then</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-d</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:#36acaa\">$1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">then</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t/opt/sublime_merge/sublime_merge </span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string variable\" style=\"color:#36acaa\">$1</span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">else</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t/opt/sublime_merge/sublime_merge </span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string variable\" style=\"color:#36acaa\">$(</span><span class=\"token string variable function\" style=\"color:#d73a49\">dirname</span><span class=\"token string variable\" style=\"color:#36acaa\"> </span><span class=\"token string variable string\" style=\"color:#e3116c\">\"</span><span class=\"token string variable string variable\" style=\"color:#36acaa\">$1</span><span class=\"token string variable string\" style=\"color:#e3116c\">\"</span><span class=\"token string variable\" style=\"color:#36acaa\">)</span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">fi</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">else</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\txmessage </span><span class=\"token string\" style=\"color:#e3116c\">\"Start Sublime Merge before to prevent losing all open tabs.\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">fi</span><br></span></code></pre></div></div>\n<p>As you can see, we check that <code>sublime_merge</code> is already running before attempting to open the repository. This is because of <a href=\"https://github.com/sublimehq/sublime_merge/issues/309\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">yet another bug in Sublime Merge</a> (at this point you might be wondering why I use this tool... I swear it's a great tool, altough it's definitely frustrating to be paying for a licence and barely receive any reply to the bug reports I create).</p>",
            "url": "https://zwyx.dev/blog/your-dotfiles-in-a-git-repo",
            "title": "Your dotfiles in a Git repo",
            "summary": "Track changes to your config files using a Git repository.",
            "date_modified": "2023-04-02T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "dotfiles",
                "git"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/own-contribution-graph",
            "content_html": "<div class=\"imageWrapper_u91s withLegend_q8O0\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/skyline-zwyx-2021-119aa1652e7ad3f01853ad0715589ac8.webp\" alt=\"GitHub Skyline\"></div><div class=\"legend_xj0V\">GitHub Skyline</div></div>\n<p>Combine your contributions from any repository on your GitHub contribution graph.</p>\n<!-- -->\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-github-contribution-graph\">The GitHub contribution graph<a href=\"https://zwyx.dev/blog/own-contribution-graph#the-github-contribution-graph\" class=\"hash-link\" aria-label=\"Direct link to The GitHub contribution graph\" title=\"Direct link to The GitHub contribution graph\" translate=\"no\">​</a></h2>\n<p>A friend and I used to work at the same company, where we were using GitHub.</p>\n<p>Every year, our contribution graphs looked nice and it was kinda rewarding.</p>\n<div class=\"imageWrapper_u91s withLegend_q8O0\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/contribution-graph-rotated--5deg-a71e5daa065d8a87b758615f58415fb6.webp\" alt=\"A contribution graph on GitHub\"></div><div class=\"legend_xj0V\">A contribution graph on GitHub</div></div>\n<p>You can do more than just look at it on your GitHub profile page: <a href=\"https://skyline.github.com/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GitHub Skyline</a> lets you generate a 3D version of it&nbsp;—&nbsp;which is the image at the top of this article. You can then download the 3D file and print it:</p>\n<div class=\"imageWrapper_u91s withLegend_q8O0\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/skyline-zwyx-2021-printed-858309e6327f06a95044176afbfada1f.webp\" alt=\"Code trophy!\"></div><div class=\"legend_xj0V\">Code trophy!</div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"not-on-github-anymore\">Not on GitHub anymore?<a href=\"https://zwyx.dev/blog/own-contribution-graph#not-on-github-anymore\" class=\"hash-link\" aria-label=\"Direct link to Not on GitHub anymore?\" title=\"Direct link to Not on GitHub anymore?\" translate=\"no\">​</a></h2>\n<p>The companies we work for now however, use other Git hosting providers. Which means... no more contribution graphs on GitHub 😢</p>\n<p>This is where Tim had a beautiful idea: create a tool that regularly scans our work repositories, and creates empty commits in a repository hosted on GitHub! Each empty commit corresponds to a commit made by ourselves in our work repositories, and is made with the same commit date.</p>\n<p>The repository gathering all these empty commits can be private on GitHub. (Although even if it's public, no content is present in it.)</p>\n<p>Brilliant!</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"usage\">Usage<a href=\"https://zwyx.dev/blog/own-contribution-graph#usage\" class=\"hash-link\" aria-label=\"Direct link to Usage\" title=\"Direct link to Usage\" translate=\"no\">​</a></h2>\n<p>The tool is <a href=\"https://npmjs.com/package/own-contribution-graph\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">own-contribution-graph</a>. To use it, simply:</p>\n<ul>\n<li class=\"\">install the package:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-g</span><span class=\"token plain\"> own-contribution-graph</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">\n<p>create the configuration file following the instructions in the <a href=\"https://npmjs.com/package/own-contribution-graph\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">ReadMe</a>,</p>\n</li>\n<li class=\"\">\n<p>run the following command:</p>\n</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">owncontributiongraph </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--config</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">path-to-the-json-config-file</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><ul>\n<li class=\"\">I added <code>own-contribution-graph</code> to the file <code>~/.nvm/default-packages</code>, so <a href=\"https://github.com/nvm-sh/nvm\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">nvm</a> automatically installs it when I install a new version of Node.</li>\n<li class=\"\">I created a Cron task that executes this command every week, and push to a private GitHub repository.</li>\n</ul></div></div>\n<p>Our contributions graphs are alive again!</p>\n<p>There is a difference though: the merge method we used to use was <em>rebase merging</em>, where all commits from a feature branch are applied one by one on the main branch. Now we use <em>squash merging</em>, were all commits are squashed together into one commit that is applied on the main branch. So less contributions appear on the graph.</p>",
            "url": "https://zwyx.dev/blog/own-contribution-graph",
            "title": "Consolidate your contribution graph",
            "summary": "Combine your contributions from any repository on your GitHub contribution graph.",
            "date_modified": "2023-03-31T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "git"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/indentation-for-accessibility",
            "content_html": "<p>It's quite obvious, for consistently looking code, spaces are best.</p>\n<p>However, does one also has consistency among the people they work with?</p>\n<p>It might not be the case: some people might have impaired vision.</p>\n<p>On an open source project, one might not even know how \"consistent\" are the people working with them.</p>\n<!-- -->\n<p>Some require such big font sizes, that <a href=\"https://www.reddit.com/r/javascript/comments/c8drjo/nobody_talks_about_the_real_reason_to_use_tabs/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">being able to set the tab width to 1 is a big deal</a>. (I am short-sighted, and although I don't have special needs other than wearing glasses, I can understand how useful it can be for others).</p>\n<p>After working years on projects using spaces, and years on projects using tabs, what's apparent to me is that <strong>for most developers, tabs or spaces don't change anything</strong>. We adapt easily to one choice or the other.</p>\n<p><strong>The only persons for whom it matters, are people with special needs. And for them, tabs are better.</strong> Shouldn't this be a done deal?</p>\n<p>This is even more true these days, as many languages now have great tools to autoformat our code. If an autoformatter is available for a language, then using it should be a priority. It is SO great to not have ever to think about indentation or formatting.</p>\n<hr>\n<p>With tabs, the code will not consistently look the same on everyone's screen anymore, but another thing will become consistent: <strong>a good experience for everyone</strong>.</p>\n<p><strong>This is for this exact reason that the web has introduced media queries</strong> like <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>prefers-color-scheme</code></a> and <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>prefers-contrast</code></a>. We want to improve accessibility of websites; source codes deserve the same.</p>\n<hr>\n<p>So, my recommendations is to set the project settings to fix the differences between operating systems that developers shouldn't have to deal with, and set some preferences like trimming trailing whitespaces.</p>\n<p>That's it. Do not set the tab width, it is a user setting.</p>\n<p>Forcing the tab width would be like forcing a bright high contrast theme for everyone in the project.</p>\n<hr>\n<p>So for example, here's an <a href=\"https://editorconfig.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>.editorconfig</code></a> file:</p>\n<div class=\"language-ini codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-ini codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># https://editorconfig.org</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">[*]</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">charset = utf-8</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">end_of_line = lf</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">indent_style = \"tab\"</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">trim_trailing_whitespace = true</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">insert_final_newline = true</span><br></span></code></pre></div></div>\n<p>And a <code>.vscode/settings.json</code> file:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"files.encoding\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"utf8\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"files.eol\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"\\n\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"files.trimTrailingWhitespace\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"files.insertFinalNewline\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"files.trimFinalNewlines\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"editor.insertSpaces\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">false</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"editor.defaultFormatter\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"esbenp.prettier-vscode\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<hr>\n<p>(We sometimes read that developers who use spaces make more money. Does it sound like BS? It probably is. Developers using spaces could simply be paid more because they're older (so have old habits). Young developers (paid less) could have answered that they use tabs because they press the Tab key when indenting. And there's more, see the comments below the <a href=\"https://stackoverflow.blog/2017/06/15/developers-use-spaces-make-money-use-tabs/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">original post</a>.)</p>\n<hr>\n<p><strong>TL;DR: I recommend to use tabs and to not force their width (and to use an automatic formatter for everything else if possible).</strong> Of cours, this doesn't apply if the project/language you work with doesn't allow it for technical reasons.</p>\n<hr>\n<p><em>This post was originally my answer to this <a href=\"https://stackoverflow.com/questions/35649847/objective-reasons-for-using-spaces-instead-of-tabs-for-indentation/75019495#75019495\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">StackOverflow question</a>.</em></p>",
            "url": "https://zwyx.dev/blog/indentation-for-accessibility",
            "title": "Indentation for accessibility",
            "summary": "The real reason to use tabs over spaces",
            "date_modified": "2023-01-05T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "code",
                "indentation",
                "formatter",
                "prettier"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/hello-docusaurus",
            "content_html": "<div style=\"text-align:center\"><p><img decoding=\"async\" loading=\"lazy\" alt=\"Docusaurus\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDIwMCAyMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBmaWxsPSIjRkZGIiBkPSJNOTkgNTJoODR2MzRIOTl6Ii8+PHBhdGggZD0iTTIzIDE2M2MtNy4zOTggMC0xMy44NDMtNC4wMjctMTcuMzAzLTEwQTE5Ljg4NiAxOS44ODYgMCAwIDAgMyAxNjNjMCAxMS4wNDYgOC45NTQgMjAgMjAgMjBoMjB2LTIwSDIzeiIgZmlsbD0iIzNFQ0M1RiIvPjxwYXRoIGQ9Ik0xMTIuOTggNTcuMzc2TDE4MyA1M1Y0M2MwLTExLjA0Ni04Ljk1NC0yMC0yMC0yMEg3M2wtMi41LTQuMzNjLTEuMTEyLTEuOTI1LTMuODg5LTEuOTI1LTUgMEw2MyAyM2wtMi41LTQuMzNjLTEuMTExLTEuOTI1LTMuODg5LTEuOTI1LTUgMEw1MyAyM2wtMi41LTQuMzNjLTEuMTExLTEuOTI1LTMuODg5LTEuOTI1LTUgMEw0MyAyM2MtLjAyMiAwLS4wNDIuMDAzLS4wNjUuMDAzbC00LjE0Mi00LjE0MWMtMS41Ny0xLjU3MS00LjI1Mi0uODUzLTQuODI4IDEuMjk0bC0xLjM2OSA1LjEwNC01LjE5Mi0xLjM5MmMtMi4xNDgtLjU3NS00LjExMSAxLjM4OS0zLjUzNSAzLjUzNmwxLjM5IDUuMTkzLTUuMTAyIDEuMzY3Yy0yLjE0OC41NzYtMi44NjcgMy4yNTktMS4yOTYgNC44M2w0LjE0MiA0LjE0MmMwIC4wMjEtLjAwMy4wNDItLjAwMy4wNjRsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgNTNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgNjNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgNzNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgODNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgOTNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgMTAzbC00LjMzIDIuNWMtMS45MjUgMS4xMTEtMS45MjUgMy44ODkgMCA1TDIzIDExM2wtNC4zMyAyLjVjLTEuOTI1IDEuMTExLTEuOTI1IDMuODg5IDAgNUwyMyAxMjNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgMTMzbC00LjMzIDIuNWMtMS45MjUgMS4xMTEtMS45MjUgMy44ODkgMCA1TDIzIDE0M2wtNC4zMyAyLjVjLTEuOTI1IDEuMTExLTEuOTI1IDMuODg5IDAgNUwyMyAxNTNsLTQuMzMgMi41Yy0xLjkyNSAxLjExMS0xLjkyNSAzLjg4OSAwIDVMMjMgMTYzYzAgMTEuMDQ2IDguOTU0IDIwIDIwIDIwaDEyMGMxMS4wNDYgMCAyMC04Ljk1NCAyMC0yMFY4M2wtNzAuMDItNC4zNzZBMTAuNjQ1IDEwLjY0NSAwIDAgMSAxMDMgNjhjMC01LjYyMSA0LjM3LTEwLjI3MyA5Ljk4LTEwLjYyNCIgZmlsbD0iIzNFQ0M1RiIvPjxwYXRoIGZpbGw9IiMzRUNDNUYiIGQ9Ik0xNDMgMTgzaDMwdi00MGgtMzB6Ii8+PHBhdGggZD0iTTE5MyAxNThjLS4yMTkgMC0uNDI4LjAzNy0uNjM5LjA2NC0uMDM4LS4xNS0uMDc0LS4zMDEtLjExNi0uNDUxQTUgNSAwIDAgMCAxOTAuMzIgMTQ4YTQuOTYgNC45NiAwIDAgMC0zLjAxNiAxLjAzNiAyNi41MzEgMjYuNTMxIDAgMCAwLS4zMzUtLjMzNiA0Ljk1NSA0Ljk1NSAwIDAgMCAxLjAxMS0yLjk4NyA1IDUgMCAwIDAtOS41OTktMS45NTljLS4xNDgtLjA0Mi0uMjk3LS4wNzctLjQ0NS0uMTE1LjAyNy0uMjExLjA2NC0uNDIuMDY0LS42MzlhNSA1IDAgMCAwLTUtNSA1IDUgMCAwIDAtNSA1YzAgLjIxOS4wMzcuNDI4LjA2NC42MzktLjE0OC4wMzgtLjI5Ny4wNzMtLjQ0NS4xMTVhNC45OTggNC45OTggMCAwIDAtOS41OTkgMS45NTljMCAxLjEyNS4zODQgMi4xNTEgMS4wMTEgMi45ODctMy43MTcgMy42MzItNi4wMzEgOC42OTMtNi4wMzEgMTQuMyAwIDExLjA0NiA4Ljk1NCAyMCAyMCAyMCA5LjMzOSAwIDE3LjE2LTYuNDEgMTkuMzYxLTE1LjA2NC4yMTEuMDI3LjQyLjA2NC42MzkuMDY0YTUgNSAwIDAgMCA1LTUgNSA1IDAgMCAwLTUtNSIgZmlsbD0iIzQ0RDg2MCIvPjxwYXRoIGZpbGw9IiMzRUNDNUYiIGQ9Ik0xNTMgMTIzaDMwdi0yMGgtMzB6Ii8+PHBhdGggZD0iTTE5MyAxMTUuNWEyLjUgMi41IDAgMSAwIDAtNWMtLjEwOSAwLS4yMTQuMDE5LS4zMTkuMDMyLS4wMi0uMDc1LS4wMzctLjE1LS4wNTgtLjIyNWEyLjUwMSAyLjUwMSAwIDAgMC0uOTYzLTQuODA3Yy0uNTY5IDAtMS4wODguMTk3LTEuNTA4LjUxOGE2LjY1MyA2LjY1MyAwIDAgMC0uMTY4LS4xNjhjLjMxNC0uNDE3LjUwNi0uOTMxLjUwNi0xLjQ5NGEyLjUgMi41IDAgMCAwLTQuOC0uOTc5QTkuOTg3IDkuOTg3IDAgMCAwIDE4MyAxMDNjLTUuNTIyIDAtMTAgNC40NzgtMTAgMTBzNC40NzggMTAgMTAgMTBjLjkzNCAwIDEuODMzLS4xMzggMi42OS0uMzc3YTIuNSAyLjUgMCAwIDAgNC44LS45NzljMC0uNTYzLS4xOTItMS4wNzctLjUwNi0xLjQ5NC4wNTctLjA1NS4xMTMtLjExMS4xNjgtLjE2OC40Mi4zMjEuOTM5LjUxOCAxLjUwOC41MThhMi41IDIuNSAwIDAgMCAuOTYzLTQuODA3Yy4wMjEtLjA3NC4wMzgtLjE1LjA1OC0uMjI1LjEwNS4wMTMuMjEuMDMyLjMxOS4wMzIiIGZpbGw9IiM0NEQ4NjAiLz48cGF0aCBkPSJNNjMgNTUuNWEyLjUgMi41IDAgMCAxLTIuNS0yLjVjMC00LjEzNi0zLjM2NC03LjUtNy41LTcuNXMtNy41IDMuMzY0LTcuNSA3LjVhMi41IDIuNSAwIDEgMS01IDBjMC02Ljg5MyA1LjYwNy0xMi41IDEyLjUtMTIuNVM2NS41IDQ2LjEwNyA2NS41IDUzYTIuNSAyLjUgMCAwIDEtMi41IDIuNSIgZmlsbD0iIzAwMCIvPjxwYXRoIGQ9Ik0xMDMgMTgzaDYwYzExLjA0NiAwIDIwLTguOTU0IDIwLTIwVjkzaC02MGMtMTEuMDQ2IDAtMjAgOC45NTQtMjAgMjB2NzB6IiBmaWxsPSIjRkZGRjUwIi8+PHBhdGggZD0iTTE2OC4wMiAxMjRoLTUwLjA0YTEgMSAwIDEgMSAwLTJoNTAuMDRhMSAxIDAgMSAxIDAgMm0wIDIwaC01MC4wNGExIDEgMCAxIDEgMC0yaDUwLjA0YTEgMSAwIDEgMSAwIDJtMCAyMGgtNTAuMDRhMSAxIDAgMSAxIDAtMmg1MC4wNGExIDEgMCAxIDEgMCAybTAtNDkuODE0aC01MC4wNGExIDEgMCAxIDEgMC0yaDUwLjA0YTEgMSAwIDEgMSAwIDJtMCAxOS44MTRoLTUwLjA0YTEgMSAwIDEgMSAwLTJoNTAuMDRhMSAxIDAgMSAxIDAgMm0wIDIwaC01MC4wNGExIDEgMCAxIDEgMC0yaDUwLjA0YTEgMSAwIDEgMSAwIDJNMTgzIDYxLjYxMWMtLjAxMiAwLS4wMjItLjAwNi0uMDM0LS4wMDUtMy4wOS4xMDUtNC41NTIgMy4xOTYtNS44NDIgNS45MjMtMS4zNDYgMi44NS0yLjM4NyA0LjcwMy00LjA5MyA0LjY0Ny0xLjg4OS0uMDY4LTIuOTY5LTIuMjAyLTQuMTEzLTQuNDYtMS4zMTQtMi41OTQtMi44MTQtNS41MzYtNS45NjMtNS40MjYtMy4wNDYuMTA0LTQuNTEzIDIuNzk0LTUuODA3IDUuMTY3LTEuMzc3IDIuNTI4LTIuMzE0IDQuMDY1LTQuMTIxIDMuOTk0LTEuOTI3LS4wNy0yLjk1MS0xLjgwNS00LjEzNi0zLjgxMy0xLjMyMS0yLjIzNi0yLjg0OC00Ljc1LTUuOTM2LTQuNjY0LTIuOTk0LjEwMy00LjQ2NSAyLjM4NS01Ljc2MyA0LjQtMS4zNzMgMi4xMy0yLjMzNSAzLjQyOC00LjE2NSAzLjM1MS0xLjk3My0uMDctMi45OTItMS41MS00LjE3MS0zLjE3Ny0xLjMyNC0xLjg3My0yLjgxNi0zLjk5My01Ljg5NS0zLjg5LTIuOTI4LjEtNC4zOTkgMS45Ny01LjY5NiAzLjYxOC0xLjIzMiAxLjU2NC0yLjE5NCAyLjgwMi00LjIyOSAyLjcyNGExIDEgMCAwIDAtLjA3MiAyYzMuMDE3LjEwMSA0LjU0NS0xLjggNS44NzItMy40ODcgMS4xNzctMS40OTYgMi4xOTMtMi43ODcgNC4xOTMtMi44NTUgMS45MjYtLjA4MiAyLjgyOSAxLjExNSA0LjE5NSAzLjA0NSAxLjI5NyAxLjgzNCAyLjc2OSAzLjkxNCA1LjczMSA0LjAyMSAzLjEwMy4xMDQgNC41OTYtMi4yMTUgNS45MTgtNC4yNjcgMS4xODItMS44MzQgMi4yMDItMy40MTcgNC4xNS0zLjQ4NCAxLjc5My0uMDY3IDIuNzY5IDEuMzUgNC4xNDUgMy42ODEgMS4yOTcgMi4xOTcgMi43NjYgNC42ODYgNS43ODcgNC43OTYgMy4xMjUuMTA4IDQuNjM0LTIuNjIgNS45NDktNS4wMzUgMS4xMzktMi4wODggMi4yMTQtNC4wNiA0LjExOS00LjEyNiAxLjc5My0uMDQyIDIuNzI4IDEuNTk1IDQuMTExIDQuMzMgMS4yOTIgMi41NTMgMi43NTcgNS40NDUgNS44MjUgNS41NTZsLjE2OS4wMDNjMy4wNjQgMCA0LjUxOC0zLjA3NSA1LjgwNS01Ljc5NCAxLjEzOS0yLjQxIDIuMjE3LTQuNjggNC4wNjctNC43NzN2LTJ6IiBmaWxsPSIjMDAwIi8+PHBhdGggZmlsbD0iIzNFQ0M1RiIgZD0iTTgzIDE4M2g0MHYtNDBIODN6Ii8+PHBhdGggZD0iTTE0MyAxNThjLS4yMTkgMC0uNDI4LjAzNy0uNjM5LjA2NC0uMDM4LS4xNS0uMDc0LS4zMDEtLjExNi0uNDUxQTUgNSAwIDAgMCAxNDAuMzIgMTQ4YTQuOTYgNC45NiAwIDAgMC0zLjAxNiAxLjAzNiAyNi41MzEgMjYuNTMxIDAgMCAwLS4zMzUtLjMzNiA0Ljk1NSA0Ljk1NSAwIDAgMCAxLjAxMS0yLjk4NyA1IDUgMCAwIDAtOS41OTktMS45NTljLS4xNDgtLjA0Mi0uMjk3LS4wNzctLjQ0NS0uMTE1LjAyNy0uMjExLjA2NC0uNDIuMDY0LS42MzlhNSA1IDAgMCAwLTUtNSA1IDUgMCAwIDAtNSA1YzAgLjIxOS4wMzcuNDI4LjA2NC42MzktLjE0OC4wMzgtLjI5Ny4wNzMtLjQ0NS4xMTVhNC45OTggNC45OTggMCAwIDAtOS41OTkgMS45NTljMCAxLjEyNS4zODQgMi4xNTEgMS4wMTEgMi45ODctMy43MTcgMy42MzItNi4wMzEgOC42OTMtNi4wMzEgMTQuMyAwIDExLjA0NiA4Ljk1NCAyMCAyMCAyMCA5LjMzOSAwIDE3LjE2LTYuNDEgMTkuMzYxLTE1LjA2NC4yMTEuMDI3LjQyLjA2NC42MzkuMDY0YTUgNSAwIDAgMCA1LTUgNSA1IDAgMCAwLTUtNSIgZmlsbD0iIzQ0RDg2MCIvPjxwYXRoIGZpbGw9IiMzRUNDNUYiIGQ9Ik04MyAxMjNoNDB2LTIwSDgzeiIvPjxwYXRoIGQ9Ik0xMzMgMTE1LjVhMi41IDIuNSAwIDEgMCAwLTVjLS4xMDkgMC0uMjE0LjAxOS0uMzE5LjAzMi0uMDItLjA3NS0uMDM3LS4xNS0uMDU4LS4yMjVhMi41MDEgMi41MDEgMCAwIDAtLjk2My00LjgwN2MtLjU2OSAwLTEuMDg4LjE5Ny0xLjUwOC41MThhNi42NTMgNi42NTMgMCAwIDAtLjE2OC0uMTY4Yy4zMTQtLjQxNy41MDYtLjkzMS41MDYtMS40OTRhMi41IDIuNSAwIDAgMC00LjgtLjk3OUE5Ljk4NyA5Ljk4NyAwIDAgMCAxMjMgMTAzYy01LjUyMiAwLTEwIDQuNDc4LTEwIDEwczQuNDc4IDEwIDEwIDEwYy45MzQgMCAxLjgzMy0uMTM4IDIuNjktLjM3N2EyLjUgMi41IDAgMCAwIDQuOC0uOTc5YzAtLjU2My0uMTkyLTEuMDc3LS41MDYtMS40OTQuMDU3LS4wNTUuMTEzLS4xMTEuMTY4LS4xNjguNDIuMzIxLjkzOS41MTggMS41MDguNTE4YTIuNSAyLjUgMCAwIDAgLjk2My00LjgwN2MuMDIxLS4wNzQuMDM4LS4xNS4wNTgtLjIyNS4xMDUuMDEzLjIxLjAzMi4zMTkuMDMyIiBmaWxsPSIjNDREODYwIi8+PHBhdGggZD0iTTE0MyA0MS43NWMtLjE2IDAtLjMzLS4wMi0uNDktLjA1YTIuNTIgMi41MiAwIDAgMS0uNDctLjE0Yy0uMTUtLjA2LS4yOS0uMTQtLjQzMS0uMjMtLjEzLS4wOS0uMjU5LS4yLS4zOC0uMzEtLjEwOS0uMTItLjIxOS0uMjQtLjMwOS0uMzhzLS4xNy0uMjgtLjIzMS0uNDNhMi42MTkgMi42MTkgMCAwIDEtLjE4OS0uOTZjMC0uMTYuMDItLjMzLjA1LS40OS4wMy0uMTYuMDgtLjMxLjEzOS0uNDcuMDYxLS4xNS4xNDEtLjI5LjIzMS0uNDMuMDktLjEzLjItLjI2LjMwOS0uMzguMTIxLS4xMS4yNS0uMjIuMzgtLjMxLjE0MS0uMDkuMjgxLS4xNy40MzEtLjIzLjE0OS0uMDYuMzEtLjExLjQ3LS4xNC4zMi0uMDcuNjUtLjA3Ljk4IDAgLjE1OS4wMy4zMi4wOC40Ny4xNC4xNDkuMDYuMjkuMTQuNDMuMjMuMTMuMDkuMjU5LjIuMzguMzEuMTEuMTIuMjIuMjUuMzEuMzguMDkuMTQuMTcuMjguMjMuNDMuMDYuMTYuMTEuMzEuMTQuNDcuMDI5LjE2LjA1LjMzLjA1LjQ5IDAgLjY2LS4yNzEgMS4zMS0uNzMgMS43Ny0uMTIxLjExLS4yNS4yMi0uMzguMzEtLjE0LjA5LS4yODEuMTctLjQzLjIzYTIuNTY1IDIuNTY1IDAgMCAxLS45Ni4xOW0yMC0xLjI1Yy0uNjYgMC0xLjMtLjI3LTEuNzcxLS43M2EzLjgwMiAzLjgwMiAwIDAgMS0uMzA5LS4zOGMtLjA5LS4xNC0uMTctLjI4LS4yMzEtLjQzYTIuNjE5IDIuNjE5IDAgMCAxLS4xODktLjk2YzAtLjY2LjI3LTEuMy43MjktMS43Ny4xMjEtLjExLjI1LS4yMi4zOC0uMzEuMTQxLS4wOS4yODEtLjE3LjQzMS0uMjMuMTQ5LS4wNi4zMS0uMTEuNDctLjE0LjMyLS4wNy42Ni0uMDcuOTggMCAuMTU5LjAzLjMyLjA4LjQ3LjE0LjE0OS4wNi4yOS4xNC40My4yMy4xMy4wOS4yNTkuMi4zOC4zMS40NTkuNDcuNzMgMS4xMS43MyAxLjc3IDAgLjE2LS4wMjEuMzMtLjA1LjQ5LS4wMy4xNi0uMDguMzItLjE0LjQ3LS4wNy4xNS0uMTQuMjktLjIzLjQzLS4wOS4xMy0uMi4yNi0uMzEuMzgtLjEyMS4xMS0uMjUuMjItLjM4LjMxLS4xNC4wOS0uMjgxLjE3LS40My4yM2EyLjU2NSAyLjU2NSAwIDAgMS0uOTYuMTkiIGZpbGw9IiMwMDAiLz48L2c+PC9zdmc+\" width=\"200\" height=\"200\" class=\"img_ev3q\"></p></div>\n<p>I've investigated different static site generators — SSGs — in order to set up a proper blog for this website. I've read about a lot of them, tried a few. My favourite by far is <a href=\"https://docusaurus.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Docusaurus</a>, the one this site is now built with.</p>\n<!-- -->\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"quick-comparison\">Quick comparison<a href=\"https://zwyx.dev/blog/hello-docusaurus#quick-comparison\" class=\"hash-link\" aria-label=\"Direct link to Quick comparison\" title=\"Direct link to Quick comparison\" translate=\"no\">​</a></h2>\n<p>As I'm working on complex web apps during the day, SSGs make evening projects feel like holiday.</p>\n<p>Still, I found that many of them require quite some tinkering before achieving desired results. In theory, to have a quick website set up, we can start from a template. But unfortunately this is where most of the pain come from. Many templates I looked at where incomplete, old, unmaintained, or simply ugly.</p>\n<p>Docusaurus is the one which has the best of both:</p>\n<ul>\n<li class=\"\">A great template to start with.</li>\n</ul>\n<p>There is <a href=\"https://docusaurus.io/docs/api/themes\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">only one</a> at the moment. It has basically everything I wanted and it's very nice. So it's much better than a choice of 100 useless templates. It might be a bit of a problem for someone really not liking it... but still, wait for the next point.</p>\n<ul>\n<li class=\"\">A great way to tinker and customise it.</li>\n</ul>\n<p>Docusaurus provides multiple ways to customise the <a href=\"https://docusaurus.io/docs/styling-layout\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">styling and layout</a>, depending on what we want to change, and how deep we want to change it. From creating React components and use them in <a href=\"https://mdxjs.com/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">MDX</a>, to the brilliant <a href=\"https://docusaurus.io/docs/swizzling\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">swizzling</a> method allowing to easily cherry pick a component to wrap it or replace it (there is an example in the <a href=\"https://zwyx.dev/blog/hello-docusaurus#add-comment-system\" class=\"\">Add comment system</a> section below).</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"setup\">Setup<a href=\"https://zwyx.dev/blog/hello-docusaurus#setup\" class=\"hash-link\" aria-label=\"Direct link to Setup\" title=\"Direct link to Setup\" translate=\"no\">​</a></h2>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>I will write about the choices I've made during the building of this blog. I won't detail every steps&nbsp;—&nbsp;official docs are better sources and will stay up to date.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"create-a-new-docusaurus-project\">Create a new Docusaurus project<a href=\"https://zwyx.dev/blog/hello-docusaurus#create-a-new-docusaurus-project\" class=\"hash-link\" aria-label=\"Direct link to Create a new Docusaurus project\" title=\"Direct link to Create a new Docusaurus project\" translate=\"no\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx create-docusaurus@latest your-project-name classic </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--typescript</span><br></span></code></pre></div></div>\n<p>This is all what's needed to have a complete website with dummy pages ready to be filled with content.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>If you're sick of having a new browser tab being opened every time you run the <code>start</code> command, append <code>--no-open</code>.</p></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"setup-prettier\">Setup Prettier<a href=\"https://zwyx.dev/blog/hello-docusaurus#setup-prettier\" class=\"hash-link\" aria-label=\"Direct link to Setup Prettier\" title=\"Direct link to Setup Prettier\" translate=\"no\">​</a></h3>\n<p><a href=\"https://prettier.io/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Prettier</a> formats Markdown well, it will be appreciated while writing blog posts.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"set-up-redirect-to-blog\">Set up redirect to <code>/blog</code><a href=\"https://zwyx.dev/blog/hello-docusaurus#set-up-redirect-to-blog\" class=\"hash-link\" aria-label=\"Direct link to set-up-redirect-to-blog\" title=\"Direct link to set-up-redirect-to-blog\" translate=\"no\">​</a></h3>\n<p>I wanted the blog to be the only content of the site for the moment. But I didn't want to <a href=\"https://docusaurus.io/docs/blog#blog-only-mode\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">make the blog base URL simply <code>/</code></a>, as all the post links would be in the format <code>&lt;domain&gt;/&lt;post&gt;</code>, which could potentially become annoying if I wanted to add other types of content to the site in the future.</p>\n<p>So I kept the blog base URL <code>/blog</code> (post links are <code>&lt;domain&gt;/blog/&lt;post&gt;</code>), and I created a redirect from <code>/</code> to <code>/blog</code>.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>Server-side redirects are best, but as I'm hosting the site on GitHub Pages, this isn't an option.</p></div></div>\n<p>Docusaurus provides a <a href=\"https://docusaurus.io/docs/next/api/plugins/@docusaurus/plugin-client-redirects\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">plugin for client-side redirects</a>:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i @docusaurus/plugin-client-redirects</span><br></span></code></pre></div></div>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">Extract from 'docusaurus.config.js'</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token literal-property property\" style=\"color:#36acaa\">plugins</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token string\" style=\"color:#e3116c\">\"@docusaurus/plugin-client-redirects\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token doc-comment comment\" style=\"color:#999988;font-style:italic\">/** </span><span class=\"token doc-comment comment keyword\" style=\"color:#00009f;font-style:italic\">@type</span><span class=\"token doc-comment comment\" style=\"color:#999988;font-style:italic\"> </span><span class=\"token doc-comment comment class-name punctuation\" style=\"color:#393A34;font-style:italic\">{</span><span class=\"token doc-comment comment class-name keyword\" style=\"color:#00009f;font-style:italic\">import</span><span class=\"token doc-comment comment class-name punctuation\" style=\"color:#393A34;font-style:italic\">(</span><span class=\"token doc-comment comment class-name string\" style=\"color:#e3116c;font-style:italic\">'@docusaurus/plugin-client-redirects'</span><span class=\"token doc-comment comment class-name punctuation\" style=\"color:#393A34;font-style:italic\">)</span><span class=\"token doc-comment comment class-name punctuation\" style=\"color:#393A34;font-style:italic\">.</span><span class=\"token doc-comment comment class-name\" style=\"color:#999988;font-style:italic\">Options</span><span class=\"token doc-comment comment class-name punctuation\" style=\"color:#393A34;font-style:italic\">}</span><span class=\"token doc-comment comment\" style=\"color:#999988;font-style:italic\"> */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">redirects</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t</span><span class=\"token keyword module\" style=\"color:#00009f\">from</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"/\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">to</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"/blog\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"deactivate-docs\">Deactivate <code>docs</code><a href=\"https://zwyx.dev/blog/hello-docusaurus#deactivate-docs\" class=\"hash-link\" aria-label=\"Direct link to deactivate-docs\" title=\"Direct link to deactivate-docs\" translate=\"no\">​</a></h3>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">Extract from 'docusaurus.config.js'</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token literal-property property\" style=\"color:#36acaa\">presets</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token string\" style=\"color:#e3116c\">\"classic\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token spread operator\" style=\"color:#393A34\">...</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">docs</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">false</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"add-metadata\">Add metadata<a href=\"https://zwyx.dev/blog/hello-docusaurus#add-metadata\" class=\"hash-link\" aria-label=\"Direct link to Add metadata\" title=\"Direct link to Add metadata\" translate=\"no\">​</a></h3>\n<p>Adding the following makes links to the blog look pretty when shared.</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">Extract from 'docusaurus.config.js'</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token literal-property property\" style=\"color:#36acaa\">presets</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token string\" style=\"color:#e3116c\">\"classic\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token spread operator\" style=\"color:#393A34\">...</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">blog</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">blogTitle</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;Blog title&gt;\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">blogDescription</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;Blog description&gt;\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><br></span></code></pre></div></div>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">Extract from 'docusaurus.config.js'</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token literal-property property\" style=\"color:#36acaa\">themeConfig</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token spread operator\" style=\"color:#393A34\">...</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">image</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;Image path&gt;\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"activate-all-feed-types\">Activate all feed types<a href=\"https://zwyx.dev/blog/hello-docusaurus#activate-all-feed-types\" class=\"hash-link\" aria-label=\"Direct link to Activate all feed types\" title=\"Direct link to Activate all feed types\" translate=\"no\">​</a></h3>\n<p>Just because I want all the shiny new stuff, I activated all the <a href=\"https://docusaurus.io/docs/next/blog#feed\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">types of feeds</a>.</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">Extract from 'docusaurus.config.js'</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token literal-property property\" style=\"color:#36acaa\">presets</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token string\" style=\"color:#e3116c\">\"classic\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token spread operator\" style=\"color:#393A34\">...</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">blog</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">feedOptions</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">type</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"all\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">title</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;Blog title&gt;\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// By default, it's in the format `&lt;Site name&gt; Blog`</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t\t</span><span class=\"token literal-property property\" style=\"color:#36acaa\">description</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;Blog description&gt;\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Same here</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"add-comment-system\">Add comment system<a href=\"https://zwyx.dev/blog/hello-docusaurus#add-comment-system\" class=\"hash-link\" aria-label=\"Direct link to Add comment system\" title=\"Direct link to Add comment system\" translate=\"no\">​</a></h3>\n<p><a href=\"https://giscus.app/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Giscus</a> is a great comment system powered by GitHub Discussions.</p>\n<p>Install its React component:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i @giscus/react</span><br></span></code></pre></div></div>\n<p>Use <a href=\"https://giscus.app/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">giscus.app</a> to set up and retrieve your configuration for Giscus, then create the file <code>src/components/GiscusComments.tsx</code>:</p>\n<div class=\"language-tsx codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">src/components/GiscusComments.tsx</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-tsx codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:#393A34\">{</span><span class=\"token imports\"> useColorMode </span><span class=\"token imports punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"@docusaurus/theme-common\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">useDocusaurusContext</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"@docusaurus/useDocusaurusContext\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">Giscus</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"@giscus/react\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">React</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"react\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">default</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">GiscusComments</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tsiteConfig</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\tcustomFields</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\tgiscusRepo</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\tgiscusRepoId</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\tgiscusCategory</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\tgiscusCategoryId</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">useDocusaurusContext</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"> colorMode </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">useColorMode</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> giscusRepo </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"string\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">||</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> giscusRepoId </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"string\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">||</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> giscusCategory </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"string\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">||</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> giscusCategoryId </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"string\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">null</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag class-name\" style=\"color:#00009f\">Giscus</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">repo</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">giscusRepo </span><span class=\"token tag script language-javascript keyword\" style=\"color:#00009f\">as</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\"> </span><span class=\"token tag script language-javascript template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token tag script language-javascript template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token tag script language-javascript template-string interpolation builtin\" style=\"color:#00009f\">string</span><span class=\"token tag script language-javascript template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag script language-javascript template-string string\" style=\"color:#e3116c\">/</span><span class=\"token tag script language-javascript template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token tag script language-javascript template-string interpolation builtin\" style=\"color:#00009f\">string</span><span class=\"token tag script language-javascript template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag script language-javascript template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">repoId</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">giscusRepoId</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">category</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">giscusCategory</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">categoryId</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">giscusCategoryId</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">mapping</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">pathname</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">strict</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">0</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">reactionsEnabled</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">1</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">emitMetadata</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">0</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">inputPosition</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">bottom</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">theme</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">colorMode</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t\t</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">lang</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">en</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag\" style=\"color:#00009f\">\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>I used Docusaurus' <a href=\"https://docusaurus.io/docs/next/api/docusaurus-config#customfields\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">custom fields</a> to retrieve Giscus configuration from Docusaurus' config (itself retrieving these values using <a href=\"https://www.npmjs.com/package/dotenv\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>dotenv</code></a>)</p></div></div>\n<p>In order to add the Giscus component to our blog posts, we are going to use Docusaurus' swizzling method to wrap one component of the template (read about swizzling <a href=\"https://docusaurus.io/docs/next/swizzling\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>):</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> run swizzle @docusaurus/theme-classic BlogPostItem </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--wrap</span><br></span></code></pre></div></div>\n<p>This command creates a new file:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">src/theme/BlogPostItem/index.js</span><br></span></code></pre></div></div>\n<p>which we can directly rename to <code>.tsx</code>:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">src/theme/BlogPostItem/index.tsx</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>Now we need to restart Docusaurus' dev server, in order for it to take the new custom component into account.</p></div></div>\n<p>This file is a wrapper for the <code>BlogPostItem</code> component. We can very easily add our new <code>GiscusComments</code> component just below it:</p>\n<div class=\"language-tsx codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">src/theme/BlogPostItem/index.tsx</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-tsx codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">useIsBrowser</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"@docusaurus/useIsBrowser\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">GiscusComments</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"@site/src/components/GiscusComments\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">BlogPostItem</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"@theme-original/BlogPostItem\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">React</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"react\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">POST_REGEX</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token regex regex-delimiter\" style=\"color:#36acaa\">/</span><span class=\"token regex regex-source language-regex anchor function\" style=\"color:#d73a49\">^</span><span class=\"token regex regex-source language-regex escape\" style=\"color:#36acaa\">\\/</span><span class=\"token regex regex-source language-regex\" style=\"color:#36acaa\">blog</span><span class=\"token regex regex-source language-regex escape\" style=\"color:#36acaa\">\\/</span><span class=\"token regex regex-source language-regex char-set class-name\" style=\"color:#36acaa\">.</span><span class=\"token regex regex-source language-regex quantifier number\" style=\"color:#36acaa\">+</span><span class=\"token regex regex-source language-regex anchor function\" style=\"color:#d73a49\">$</span><span class=\"token regex regex-delimiter\" style=\"color:#36acaa\">/</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">default</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">BlogPostItemWrapper</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">props</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> isBrowser </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">useIsBrowser</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag class-name\" style=\"color:#00009f\">BlogPostItem</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag spread punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag spread operator\" style=\"color:#393A34\">...</span><span class=\"token tag spread\" style=\"color:#00009f\">props</span><span class=\"token tag spread punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\">isBrowser </span><span class=\"token operator\" style=\"color:#393A34\">&amp;&amp;</span><span class=\"token plain\"> </span><span class=\"token dom variable\" style=\"color:#36acaa\">window</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">location</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">pathname</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">match</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token constant\" style=\"color:#36acaa\">POST_REGEX</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">&amp;&amp;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">style</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\"> marginTop</span><span class=\"token tag script language-javascript operator\" style=\"color:#393A34\">:</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\"> </span><span class=\"token tag script language-javascript string\" style=\"color:#e3116c\">\"32px\"</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\"> </span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag class-name\" style=\"color:#00009f\">GiscusComments</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">style</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\"> marginTop</span><span class=\"token tag script language-javascript operator\" style=\"color:#393A34\">:</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\"> </span><span class=\"token tag script language-javascript string\" style=\"color:#e3116c\">\"-24px\"</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\"> </span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>Details</div><div class=\"admonitionContent_BuS1\"><ul>\n<li class=\"\">We check that the <code>pathname</code> matches <code>/blog/...</code> to not show the Giscus component below all the posts on the main listing page (<code>/blog</code>).</li>\n<li class=\"\"><code>isBrowser</code> is to prevent evaluating <code>window</code> at build time, which would creates an error.</li>\n</ul></div></div>\n<p>Last thing, to be done once your local testing is finish, is to add a file <code>giscus.json</code> at the root of the repository to <a href=\"https://github.com/giscus/giscus/blob/main/ADVANCED-USAGE.md#giscusjson\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">prevent other websites from showing your discussions</a>:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">giscus.json</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">\"origins\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token string\" style=\"color:#e3116c\">\"https://&lt;your-domain&gt;\"</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<p>Giscus will stop showing on your <code>localhost:3000</code> after this file is committed.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"automatically-deploy-to-github-pages-netlify-etc\">Automatically deploy to GitHub Pages, Netlify, etc.<a href=\"https://zwyx.dev/blog/hello-docusaurus#automatically-deploy-to-github-pages-netlify-etc\" class=\"hash-link\" aria-label=\"Direct link to Automatically deploy to GitHub Pages, Netlify, etc.\" title=\"Direct link to Automatically deploy to GitHub Pages, Netlify, etc.\" translate=\"no\">​</a></h3>\n<p>The site is deployed on very push to the <code>main</code> branch. The <a href=\"https://github.com/Zwyx/zwyx.dev/blob/main/.github/workflows/build-website.yml\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">code</a> should be self-explanatory with enough knowledge on <a href=\"https://docs.github.com/en/actions\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GitHub Actions</a> and <a href=\"https://docs.github.com/en/pages\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GitHub Pages</a>.</p>",
            "url": "https://zwyx.dev/blog/hello-docusaurus",
            "title": "Hello Docusaurus!",
            "summary": "A few tips on how I built this site with the great static site generator Docusaurus",
            "date_modified": "2022-11-22T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "docusaurus",
                "static site generator",
                "blog"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/git-hash-miner",
            "content_html": "<div class=\"imageWrapper_u91s withLegend_q8O0\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/git-hash-miner-b17862480cfb6c1f69e91a500df618c7.webp\" alt=\"Git hashes mined with Git Hash Miner\"></div><div class=\"legend_xj0V\">Git hashes mined with Git Hash Miner</div></div>\n<p>Following the same principle that Bitcoin uses for its proof of work, we can \"mine\" our Git commit hashes too!</p>\n<!-- -->\n<hr>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>About hash mining</div><div class=\"admonitionContent_BuS1\"><p>To be accepted in the Bitcoin blockchain, the numerical value of a block's hash needs to be lower than a certain number. This make the hash starting with some zeros. This number is regularly reduced, as computers get more powerful; the smaller the number, the harder it is to find a winning hash.</p></div></div>\n<p>We can do the same with Git commit hashes: mine them to make them start with a particular prefix. Or end with a particular suffix, or any other rule we'd like.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"how-are-git-commit-hashes-generated\">How are Git commit hashes generated<a href=\"https://zwyx.dev/blog/git-hash-miner#how-are-git-commit-hashes-generated\" class=\"hash-link\" aria-label=\"Direct link to How are Git commit hashes generated\" title=\"Direct link to How are Git commit hashes generated\" translate=\"no\">​</a></h2>\n<p>Roughly, when a commit is created, Git take these details:</p>\n<ul>\n<li class=\"\">the hash of the parent commit,</li>\n<li class=\"\">the hash of the tree object,</li>\n<li class=\"\">the author's name and email address,</li>\n<li class=\"\">the commit creation date,</li>\n<li class=\"\">the committer's name and email address,</li>\n<li class=\"\">the committing date (which will different than the creation date after, for instance, an amend or a rebase),</li>\n<li class=\"\">the PGP signature if the commit has been signed,</li>\n<li class=\"\">the title and body of the commit,</li>\n</ul>\n<p>and generates a SHA-1 hash with them. That's the commit hash.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>To \"mine\" a commit hash, we need to generate multiple hashes until we find one that respects our rule (for instance, starts with a particular prefix). But to make each hash different than the previous one, we need to change something in the details listed above.</p></div></div>\n<p>I made the package <a href=\"https://npmjs.com/package/git-hash-miner\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\"><code>git-hash-miner</code></a>, which appends a hexadecimal number to the committer name of the last commit, regenerates the SHA-1 hash of the commit details, and continues by incrementing the hexadecimal number at each round until a winning hash is found.</p>\n<p>Once it is found, <code>git-hash-miner</code> can automatically amend the commit to add the hexadecimal number to the committer name, and let Git handle the commit and generate the SHA-1, which will be the winning one found just before.</p>\n<p>The committer's name is usually not shown by git clients, so it's not an issue to have it modified.</p>\n<p>And even when it's shown, by Sublime Merge for instance, I believe it's not a big deal to have the committer's name followed by a bunch of hexadecimal digits:</p>\n<div class=\"imageWrapper_u91s withLegend_q8O0\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"data:image/webp;base64,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\" alt=\"Hexadecimal number appended to the committer name\"></div><div class=\"legend_xj0V\">Hexadecimal number appended to the committer name</div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"mine-your-commit-hashes\">Mine your commit hashes!<a href=\"https://zwyx.dev/blog/git-hash-miner#mine-your-commit-hashes\" class=\"hash-link\" aria-label=\"Direct link to Mine your commit hashes!\" title=\"Direct link to Mine your commit hashes!\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">Install <code>git-hash-miner</code>:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-g</span><span class=\"token plain\"> git-hash-miner</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">Then, in a Git repository, run this command after having created a commit:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gmr </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">--auto-amend</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\">-a</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">target</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><br></span></code></pre></div></div>\n<p>where <code>target</code> is the prefix we want the commit hash to start with, and <code>-a</code> automatically amends the commit if the target is found. Do some tests without <code>-a</code> first.</p>\n<p>Example: the command <code>gmr -a badc0de</code> will search for a commit hash starting with <code>badc0de</code> then automatically amend the previous commit once it's found.</p>\n<p><code>git-hash-miner</code> creates a worker for each CPU core on your machine. Roughly, you should be able to mine a hash with a prefix of up to 6 or 7 characters in a few minutes.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>Pro tip</div><div class=\"admonitionContent_BuS1\"><p>I added <code>git-hash-miner</code> to the file <code>~/.nvm/default-packages</code>, so <a href=\"https://github.com/nvm-sh/nvm\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">nvm</a> automatically installs it when I install a new version of Node.</p></div></div>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>Note that there are other projects of this kind around the internet, some of them more performant as they use GPUs. They are, however, more complex to install than <code>git-hash-miner</code>, which is a simple Node script.</p></div></div>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>Also note that at the moment, if you sign your commits, then your signature is dropped when using <code>git-hash-miner</code>.</p></div></div>\n<hr>\n<p>Have fun mining!</p>",
            "url": "https://zwyx.dev/blog/git-hash-miner",
            "title": "Git Hash Miner: mine your commit hashes!",
            "summary": "Following the same principle that Bitcoin uses for its proof of work, we can \"mine\" our Git commit hashes too!",
            "date_modified": "2022-03-05T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "git",
                "commit",
                "hash"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/google-authenticator-export-format",
            "content_html": "<p>If you use Google Authenticator and scan QR codes to create new Time-based One-Time Passwords, you might have noticed that their names are sometimes in the form <code>Issuer (Account name)</code>. Google Authenticator allows to rename the <code>Account name</code> part, but not the <code>Issuer</code>.</p>\n<p>The following explains how to rename the <code>Issuer</code>, or even get rid of it.</p>\n<!-- -->\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"requirements\">Requirements<a href=\"https://zwyx.dev/blog/google-authenticator-export-format#requirements\" class=\"hash-link\" aria-label=\"Direct link to Requirements\" title=\"Direct link to Requirements\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">\n<p>The Google Protocol Buffers tool, which can be found <a href=\"https://developers.google.com/protocol-buffers\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>, or in <a href=\"https://github.com/Zwyx/google-authenticator-export-format\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">this repository</a>.</p>\n</li>\n<li class=\"\">\n<p>The <code>OtpMigration.proto</code> file present <a href=\"https://github.com/qistoph/otp_export/blob/master/OtpMigration.proto\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a> — thank you to the author — or <a href=\"https://github.com/Zwyx/google-authenticator-export-format\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</p>\n</li>\n<li class=\"\">\n<p>A way to scan and create QR codes — <code>zbarcam</code> and <code>qrencode</code> for instance.</p>\n</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"export-edit-and-import-google-authenticators-data\">Export, edit, and import Google Authenticator's data<a href=\"https://zwyx.dev/blog/google-authenticator-export-format#export-edit-and-import-google-authenticators-data\" class=\"hash-link\" aria-label=\"Direct link to Export, edit, and import Google Authenticator's data\" title=\"Direct link to Export, edit, and import Google Authenticator's data\" translate=\"no\">​</a></h2>\n<p>In Google Authenticator settings, export your accounts. The app will display a QR code containing your TOTP data encoded with Protocol Buffers and URL encoded base64. If you have lots of accounts, then multiple QR codes will be created.</p>\n<p>Use the next command with one QR code at a time.</p>\n<p>I use ZSH. You might have to tweak the commands if you use a different shell.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">zbarcam </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">sed</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/QR-Code://'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">sed</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/otpauth-migration:\\/\\/offline?data=//'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">sed</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-e</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/%2B/+/ig'</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-e</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/%2F/\\//ig'</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-e</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/%3D/=/ig'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tbase64 </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-d</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tprotoc </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--decode</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\">MigrationPayload OtpMigration.proto </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> secrets</span><br></span></code></pre></div></div>\n<p>If you retrieve the data from the QR code in another way, you can replace the first line by:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Note the space before `echo`, it prevents the line</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># to be saved in the command history, as it contains secret data</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"> </span><span class=\"token builtin class-name\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;qr-code-data&gt;\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><br></span></code></pre></div></div>\n<p>Now, you can open the <code>secrets</code> file and edit it as you want. For each entry, you can rename the <code>issuer</code>, or delete it.</p>\n<p>Then run the following to recreate a QR code and display it on your screen, which you will scan with Google Authenticator using the \"Import accounts\" feature present in the settings.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">cat</span><span class=\"token plain\"> secrets </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tprotoc </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--encode</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\">MigrationPayload OtpMigration.proto </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tbase64 </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-w</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">sed</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-e</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/+/%2B/ig'</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-e</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/\\//%2F/ig'</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-e</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/=/%3D/ig'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">sed</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'s/^/otpauth-migration:\\/\\/offline?data=/'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token function\" style=\"color:#d73a49\">xargs</span><span class=\"token plain\"> </span><span class=\"token builtin class-name\">echo</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">\\</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tqrencode </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-t</span><span class=\"token plain\"> utf8 </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-o</span><span class=\"token plain\"> -</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-caution admonition_xJq3 alert alert--warning\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"></path></svg></span>caution</div><div class=\"admonitionContent_BuS1\"><p>Keep in mind that this data is as secret as your passwords. Do not play with QR codes in a public place where you could be seen by a security cameras, or people with phones.</p></div></div>",
            "url": "https://zwyx.dev/blog/google-authenticator-export-format",
            "title": "Google Authenticator export format",
            "summary": "How to rename the issuers of your Google Authenticator one-time passwords.",
            "date_modified": "2021-11-25T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "google authenticator",
                "totp",
                "one time password"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/archiving-git-branches",
            "content_html": "<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/branches-1bd3552051ab477a198c0f99fce33fd2.webp\" alt=\"Tree branches\"></div></div>\n<p>Reduce the amount of branches without necessarily deleting them</p>\n<!-- -->\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>This article contains ideas and suggestions for an more advanced use of Git; it's intended for people who already know its basic usage.</p></div></div>\n<hr>\n<p>When working on a code base, alone or with a team, we can end up with a lot of branches that are inactive. They are experiments that have never been completed, for instance.</p>\n<p>The presence of these forgotten branches can be bothering:</p>\n<ul>\n<li class=\"\">when using the CLI, auto-completion for <code>git checkout</code> will be cluttered;</li>\n<li class=\"\">when using an interface such as GitHub, to select a branch in order to create a pull request or run an action, the list will be long;</li>\n<li class=\"\">generally, you may find that having loads of branches in your repo is like having loads <em>TODOs-that-will-never-be-done</em> on your TODO list. Or loads of <em>\"in progress\"</em> tickets on a Kanban board.</li>\n</ul>\n<p>However, deleting these branches might not be possible. Some could be useful in the future. Some people in your team might want to delete them while others want to keep them.</p>\n<p>A solution is to “archive” these branches by storing them separately from the normal git branches. The archived branches won't show up in <code>git branch</code> anymore, but they will stay available if someone wants to resume work on them.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"git-refs\">Git refs<a href=\"https://zwyx.dev/blog/archiving-git-branches#git-refs\" class=\"hash-link\" aria-label=\"Direct link to Git refs\" title=\"Direct link to Git refs\" translate=\"no\">​</a></h2>\n<p>Git stores local branches in <code>refs/heads</code>. For instance, a branch called branch will be in <code>refs/heads/branch</code>.</p>\n<p>Remote branches are in <code>refs/remote/&lt;remote-name&gt;</code>. For instance, <code>refs/remote/origin/branch</code>. There are also <code>refs/tags</code>, <code>refs/stash</code>, <code>refs/notes</code>.</p>\n<p>To archive a branch, we can place them in a newly created ref, for instance: <code>refs/experiments</code>.</p>\n<p>If we are in a team we can add a sub-level with the name of the developers: <code>refs/experiments/&lt;developer-name&gt;</code>.</p>\n<p>The following sections explains the commands allowing us to achieve this.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"archive-a-branch\">Archive a branch<a href=\"https://zwyx.dev/blog/archiving-git-branches#archive-a-branch\" class=\"hash-link\" aria-label=\"Direct link to Archive a branch\" title=\"Direct link to Archive a branch\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">\n<p>Save the branch in the <code>experiments</code> ref:</p>\n<p><code>git update-ref refs/experiments/alice/branch branch</code></p>\n</li>\n<li class=\"\">\n<p>Push the experiment to the remote:</p>\n<p><code>git push origin refs/experiments/alice/branch</code></p>\n</li>\n<li class=\"\">\n<p>Delete the original branch, locally and on the remote:</p>\n<p><code>git branch -d branch</code></p>\n<p><code>git push -d origin branch</code></p>\n</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"restore-a-branch\">Restore a branch<a href=\"https://zwyx.dev/blog/archiving-git-branches#restore-a-branch\" class=\"hash-link\" aria-label=\"Direct link to Restore a branch\" title=\"Direct link to Restore a branch\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">\n<p>Create a branch that points to the same commit than the experiment:</p>\n<p><code>git checkout -b branch refs/experiments/alice/branch</code></p>\n</li>\n<li class=\"\">\n<p>Optional, delete the experiment, locally and on the remote:</p>\n<p><code>git update-ref -d refs/experiments/alice/branch</code></p>\n<p><code>git push -d origin refs/experiments/alice/branch</code></p>\n</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"other-useful-commands\">Other useful commands<a href=\"https://zwyx.dev/blog/archiving-git-branches#other-useful-commands\" class=\"hash-link\" aria-label=\"Direct link to Other useful commands\" title=\"Direct link to Other useful commands\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">\n<p>View all experiments:</p>\n<p><code>git for-each-ref | grep refs/experiments</code></p>\n</li>\n<li class=\"\">\n<p>Push all experiments to the remote:</p>\n<p><code>git push origin \"refs/experiments/*\"</code></p>\n</li>\n<li class=\"\">\n<p>Fetch all experiments present on the remote:</p>\n<p><code>git fetch origin \"refs/experiments/*:refs/experiments/*\"</code></p>\n<p>(or:</p>\n<p><code>git fetch origin \"+refs/experiments/\\*:refs/experiments/\\*\"</code></p>\n<p>but careful: <strong>this will erase all experiments present locally</strong>)</p>\n</li>\n<li class=\"\">\n<p>See everything that's in refs on a remote repository:</p>\n<p><code>git clone --mirror &lt;repo&gt;</code></p>\n<p><code>cd &lt;repo&gt;</code></p>\n<p><code>git for-each-ref</code></p>\n<p>Doing this on a GitHub repository which has had pull requests, you will notice that GitHub creates refs/pull and stores all pull requests in it.</p>\n</li>\n</ul>",
            "url": "https://zwyx.dev/blog/archiving-git-branches",
            "title": "Tidy up a Git repo by archiving branches",
            "summary": "Reduce the amount of branches without necessarily deleting them.",
            "date_modified": "2021-10-23T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "git",
                "branch",
                "archive"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/shared-password-stores",
            "content_html": "<div class=\"imageWrapper_u91s withLegend_q8O0\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/main-47cd28480cb4393dd05f6181fe258cf5.webp\" alt=\"Password store layout example\"></div><div class=\"legend_xj0V\">Password store layout example</div></div>\n<p>In this post, we will use <code>pass</code> to set up a complete, shared and segmented, professional password store.</p>\n<!-- -->\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>This post contains ideas and suggestions for a more advanced use of <code>pass</code>; it's intended for people who already know its basic usage.</p></div></div>\n<hr>\n<p>Before all, why do I use a trivial command line tool instead of one of the many fancy password managers available?</p>\n<p>Because I believe critical things need to be kept simple.</p>\n<p>They need to be open source and have proven themselves by being maintained, audited, and used by many.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>Even a serious security company is capable of inadvertently exposing a backdoor on their users' machines <a href=\"https://bugs.chromium.org/p/project-zero/issues/detail?id=693&amp;redir=1\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">allowing remote code execution and the theft of your passwords</a> (← I really recommend the reading of this story).</p></div></div>\n<p><code>pass</code> is <a href=\"https://git.zx2c4.com/password-store/tree/src/password-store.sh\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">one bash script</a> relying on <a href=\"https://gnupg.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GPG</a>. Both tools are open and have been used extensively for a while.</p>\n<p>There are some GUIs for <code>pass</code>, some browser extensions, mobile apps, etc. but choose them carefully: the more we add, the larger the <a href=\"https://github.com/IJHack/QtPass/issues/338\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">attack surface</a> becomes.</p>\n<p>I use Termux on Android, which provides <code>pass</code> in its <a href=\"https://github.com/termux/termux-packages\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">packages</a>. It doesn't autofill credentials, but apps and websites are good at remembering who you are these days, so I don't need to log in often.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"retrieve-a-password\">Retrieve a password<a href=\"https://zwyx.dev/blog/shared-password-stores#retrieve-a-password\" class=\"hash-link\" aria-label=\"Direct link to Retrieve a password\" title=\"Direct link to Retrieve a password\" translate=\"no\">​</a></h2>\n<p>First, a little tweak for the retrieval of a password.</p>\n<p>The default command — <code>pass -c &lt;password-name&gt;</code> — outputs the password in the clipboard and clears it after 45 seconds.</p>\n<p>However, I have a clipboard manager that keeps everything I put in the clipboard.</p>\n<p>To prevent passwords from being recorded, I wrote a command that outputs them in the <em>X selection</em> instead of the clipboard. To paste them, I click the middle mouse button, instead of using Ctrl-V.</p>\n<p>To do that, add the following in your <code>.zshrc</code> (you might have to make some modifications if you're using a different shell):</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function-name function\" style=\"color:#d73a49\">p</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Insert the password into the X selection</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># (also called primary, see `man xclip`)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tpass </span><span class=\"token variable\" style=\"color:#36acaa\">$1</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">head</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-n</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">tr</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-d</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string entity\" style=\"color:#36acaa\">\\n</span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> xclip</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Countdown</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">for</span><span class=\"token plain\"> </span><span class=\"token for-or-select variable\" style=\"color:#36acaa\">i</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">in</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token number\" style=\"color:#36acaa\">5</span><span class=\"token punctuation\" style=\"color:#393A34\">..</span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">do</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token builtin class-name\">printf</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string entity\" style=\"color:#36acaa\">\\r</span><span class=\"token string variable\" style=\"color:#36acaa\">$i</span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token function\" style=\"color:#d73a49\">sleep</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">done</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Clear the selection</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token builtin class-name\">echo</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-n</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> xclip</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token builtin class-name\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"</span><span class=\"token string entity\" style=\"color:#36acaa\">\\r</span><span class=\"token string\" style=\"color:#e3116c\">✔\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Associate the completer of `pass` to the command `p`, allowing</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># the Tab key to be used with `p` to autocomplete the passwords names</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">compdef _pass p</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Note: use `p` only to retrieve an existing password,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># not with other `pass` commands (`generate`, etc.)</span><br></span></code></pre></div></div>\n<p>Here it is in action:</p>\n<!-- -->\n<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/p-59e0936ad3963b402dad61fbe18813b6.gif\" alt=\"The p command\"></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"set-up-more-than-one-password-store\">Set up more than one password store<a href=\"https://zwyx.dev/blog/shared-password-stores#set-up-more-than-one-password-store\" class=\"hash-link\" aria-label=\"Direct link to Set up more than one password store\" title=\"Direct link to Set up more than one password store\" translate=\"no\">​</a></h2>\n<p>It's possible to have two completely separated password stores by creating a new command for the second one. The new command, <code>passpro</code> for instance, will use a different directory:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function-name function\" style=\"color:#d73a49\">passpro</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Set a different password store directory,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># then run `pass` with all the arguments received by `passpro`</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token assign-left variable\" style=\"color:#36acaa\">PASSWORD_STORE_DIR</span><span class=\"token operator\" style=\"color:#393A34\">=~</span><span class=\"token plain\">/.password-store-pro pass </span><span class=\"token variable\" style=\"color:#36acaa\">$@</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function-name function\" style=\"color:#d73a49\">_passpro</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Same idea for the completer</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token assign-left variable\" style=\"color:#36acaa\">PASSWORD_STORE_DIR</span><span class=\"token operator\" style=\"color:#393A34\">=~</span><span class=\"token plain\">/.password-store-pro _pass</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Then we associate the two</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">compdef _passpro passpro</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># And we can have the same short command to retrieve a password:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function-name function\" style=\"color:#d73a49\">pp</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token assign-left variable\" style=\"color:#36acaa\">PASSWORD_STORE_DIR</span><span class=\"token operator\" style=\"color:#393A34\">=~</span><span class=\"token plain\">/.password-store-pro p </span><span class=\"token variable\" style=\"color:#36acaa\">$@</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">compdef _passpro pp</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>tip</div><div class=\"admonitionContent_BuS1\"><p>Having two different pass commands is my preference, but there are other ways to have multiple stores. For instance, you could make use of <code>.gitignore</code> or git submodules to have the second store inside the first one.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"share-a-password-store\">Share a password store<a href=\"https://zwyx.dev/blog/shared-password-stores#share-a-password-store\" class=\"hash-link\" aria-label=\"Direct link to Share a password store\" title=\"Direct link to Share a password store\" translate=\"no\">​</a></h2>\n<p>We can share a password store with many people, while still being able to fine tune who has access to what.</p>\n<ul>\n<li class=\"\">We create a subfolder for each team, for instance a <code>devs</code> subfolder and a <code>support</code> subfolder:</li>\n</ul>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">~/.password-store-pro</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">├── devs</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── databasePassword</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── serverSshKey</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   └── stackCredentials</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">└── support</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    ├── supportPlatformPassword</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    └── supportEmailPassword</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">Inside each, we put a <code>.gpg-id</code> file listing the PGP key UIDs of the persons having access to the content of this subfolder:</li>\n</ul>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">~/.password-store-pro</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">├── devs          ┌───────────────────────────┐</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── .gpg-id → │ Alice &lt;alice@example.com&gt; │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   │             │ Bob &lt;bob@example.com&gt;     │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   │             └───────────────────────────┘</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── databasePassword    \\</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── serverSshKey         &gt; Each file is encrypted</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   └── stackCredentials    /  for Alice and Bob</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">└── support       ┌───────────────────────────┐</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    ├── .gpg-id → │ Alice &lt;alice@example.com&gt; │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    │             │ Carl &lt;carl@example.com&gt;   │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    │             │ David &lt;david@example.com&gt; │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    │             └───────────────────────────┘</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    ├── supportPlatformPassword   \\ Each file is encrypted for</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    └── supportEmailPassword      / Alice, Carl and David</span><br></span></code></pre></div></div>\n<p>And that's basically it.</p>\n<p>There are a few other things to help us:</p>\n<ul>\n<li class=\"\">\n<p>In the root directory, a <code>.public-keys</code> folder contains the public PGP keys of all the persons having access to the store. That allows the creation of new passwords that will be decryptable by the persons/teams we want.</p>\n</li>\n<li class=\"\">\n<p>The <code>.gpg-id</code> file in the root directory lists who has access to the secrets in this directory — and any subfolder which doesn't have its own <code>.gpg-id</code>. We don't use it in this example.</p>\n</li>\n<li class=\"\">\n<p>Finally, the bash script <code>encrypt.sh</code> is used to display detailed information about the password store, and to reencrypt it when a new user is given access to the store. Indeed, when a new user is added, every passwords he will have access to needs to be reencrypted for his public key. This process is explain later in this article.</p>\n</li>\n</ul>\n<p>So here's our complete, shared and segmented, professional password store:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">~/.password-store-pro</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">├── .public-keys</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── alice.asc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── bob.asc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── carl.asc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   └── david.asc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">├── devs          ┌───────────────────────────┐</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── .gpg-id → │ Alice &lt;alice@example.com&gt; │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   │             │ Bob &lt;bob@example.com&gt;     │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   │             └───────────────────────────┘</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── databasePassword</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── serverSshKey</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   └── stackCredentials</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">├── support       ┌───────────────────────────┐</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── .gpg-id → │ Alice &lt;alice@example.com&gt; │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   │             │ Carl &lt;carl@example.com&gt;   │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   │             │ David &lt;david@example.com&gt; │</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   │             └───────────────────────────┘</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   ├── supportPlatformPassword</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">│   └── supportEmailPassword</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">├── .gpg-id</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">└── encrypt.sh</span><br></span></code></pre></div></div>\n<p>The content of encrypt.sh can be found <a href=\"https://gist.github.com/Zwyx/aecea360db2c50a058a9b1f0c5287b45\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</p>\n<p>Now, let's see how to use the password store.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"add-a-new-password-to-the-shared-store\">Add a new password to the shared store<a href=\"https://zwyx.dev/blog/shared-password-stores#add-a-new-password-to-the-shared-store\" class=\"hash-link\" aria-label=\"Direct link to Add a new password to the shared store\" title=\"Direct link to Add a new password to the shared store\" translate=\"no\">​</a></h2>\n<p>Now that we have the structure of our shared store, let's create a password.</p>\n<ul>\n<li class=\"\">First, we import the all public keys to our keyring:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--import</span><span class=\"token plain\"> ~/.password-store-pro/.public-keys/*.asc</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">And we trust them — the following needs to be done for each keys:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg --edit-key </span><span class=\"token string\" style=\"color:#e3116c\">\"&lt;key-uid&gt;\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> trust</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Your decision? </span><span class=\"token number\" style=\"color:#36acaa\">5</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> quit</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">Optionally, we create a new Git branch. As the password store is a Git repository, we can run Git commands on this repository with <code>pass git</code>, for instance:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">passpro </span><span class=\"token function\" style=\"color:#d73a49\">git</span><span class=\"token plain\"> checkout </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-b</span><span class=\"token plain\"> new-password</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">Choose in which subfolder to put the new password, and generate it:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">passpro generate </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-n</span><span class=\"token plain\"> devs/cloudPlatformCredentials </span><span class=\"token number\" style=\"color:#36acaa\">20</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">Push the branch and create a pull request.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"give-access-to-the-password-store-to-a-new-user\">Give access to the password store to a new user<a href=\"https://zwyx.dev/blog/shared-password-stores#give-access-to-the-password-store-to-a-new-user\" class=\"hash-link\" aria-label=\"Direct link to Give access to the password store to a new user\" title=\"Direct link to Give access to the password store to a new user\" translate=\"no\">​</a></h2>\n<p>As stated before, when a new user is added, every passwords he will have access to needs to be reencrypted for his public key.</p>\n<ul>\n<li class=\"\">\n<p>First, we add the user's public key to the <code>.public-keys</code> directory.</p>\n</li>\n<li class=\"\">\n<p>Then, we import it to our keyring:</p>\n</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--import</span><span class=\"token plain\"> .public-keys/*.asc</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">We find its uid:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg --list-keys</span><br></span></code></pre></div></div>\n<p>For instance, the uid can be: <code>Elie &lt;elie@example.com&gt;</code></p>\n<ul>\n<li class=\"\">We trust the public key:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg --edit-key </span><span class=\"token string\" style=\"color:#e3116c\">\"Elie &lt;elie@example.com&gt;\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> trust</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Your decision? </span><span class=\"token number\" style=\"color:#36acaa\">5</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">gpg</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> quit</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">We add the key's uid in the <code>.gpg-id</code> file of each subfolder the new user needs to have access to; for instance, let's say that the new user is a developer, we add the key's uid in <code>devs/.gpg-id</code>:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Alice </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">alice@example.com</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Bob </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">bob@example.com</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Elie </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">elie@example.com</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><br></span></code></pre></div></div>\n<ul>\n<li class=\"\">We now reencrypt all passwords and secrets for the new user:</li>\n</ul>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">./encrypt.sh devs</span><br></span></code></pre></div></div>\n<p>This will list the persons for whom the <code>devs</code> subfolder will be encrypted, and ask you to confirm:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Password store root directory:</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token string\" style=\"color:#e3116c\">'/home/user/.password-store-pro'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token number\" style=\"color:#36acaa\">1</span><span class=\"token plain\"> subfolders will be encrypted.</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token string\" style=\"color:#e3116c\">'devs'</span><span class=\"token plain\"> will be encrypted for:</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tAlice </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">alice@example.com</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tBob </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">bob@example.com</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tElie </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">elie@example.com</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">Proceed? </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">y/N</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><br></span></code></pre></div></div>\n<p><code>./encrypt.sh</code> can also be run without any arguments, to reencrypt the whole store. However, remember that you need access to a password in order to be able to encrypt it for a new user, as you need to decrypt it first.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>Decrypting passwords stays as easy than it is with a personal password store. Note that Windows users who only need read access to the passwords can install Gpg4win - instead of a non-official version of Pass.</p></div></div>\n<hr>\n<p>I hope you enjoyed this article, have fun with Pass!</p>",
            "url": "https://zwyx.dev/blog/shared-password-stores",
            "title": "Multiple shared password stores with Git and pass",
            "summary": "Use pass to set up a complete, shared and segmented, professional password store.",
            "date_modified": "2021-06-27T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "password",
                "password manager",
                "pass"
            ]
        },
        {
            "id": "https://zwyx.dev/blog/react-nfc",
            "content_html": "<div class=\"theme-admonition theme-admonition-danger admonition_xJq3 alert alert--danger\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z\"></path></svg></span>Warning: this article is old!</div><div class=\"admonitionContent_BuS1\"><p>This article has been written in 2019. I stopped working with NFC on mobile a few years later.</p></div></div>\n<div class=\"imageWrapper_u91s\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/react-nfc-2df0d3ca1a564f805081146463d38c26.webp\" alt=\"React NFC\"></div></div>\n<p><a href=\"https://capacitor.ionicframework.com/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Ionic Capacitor</a> and <a href=\"https://cordova.apache.org/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Apache Cordova</a> are two common wrappers to create a hybrid mobile application from a web application. The main advantage over a native mobile app is the re-usability of the code — and the development languages being HTML, CSS, and JavaScript, if we like them.</p>\n<!-- -->\n<p>However, hybrid apps are not always the best choice and some research is necessary to determine which technology is best for a particular purpose.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>One thing to keep in mind though, is that using a mobile wrapper to create a hybrid app which is nothing more than a shell for a website, is <a href=\"https://stackoverflow.com/questions/5478848/does-apple-reject-mobile-web-shell-applications\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">not well accepted</a>.</p></div></div>\n<hr>\n<p>Let's say we have a nice responsive web app which works already great on a mobile browser, and which would be even greater if it could use NFC features on mobile devices.</p>\n<p>As we cannot access NFC from a web browser (<a href=\"https://w3c.github.io/web-nfc/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">yet</a>), we need to create a mobile app.</p>\n<p>As our web app is already designed to be displayed well on small screens (responsive), creating a hybrid mobile app from it will be much easier and quicker than recreating a native mobile app from scratch (or more than one: one for each platform we want to support…).</p>\n<p>To demonstrate this, we will start by <a href=\"https://create-react-app.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">creating a React app</a> — with TypeScript — add the <a href=\"https://github.com/chariotsolutions/phonegap-nfc\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">PhoneGap NFC plugin</a>, then wrap our app with Capacitor or Cordova. We will need Node and npm (use a Node version manager, like <a href=\"https://github.com/nvm-sh/nvm\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">nvm</a>), the Android SDK and Android Studio.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"web-app\">Web App<a href=\"https://zwyx.dev/blog/react-nfc#web-app\" class=\"hash-link\" aria-label=\"Direct link to Web App\" title=\"Direct link to Web App\" translate=\"no\">​</a></h2>\n<p>First, we create a new React app by running:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx create-react-app react-nfc </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--typescript</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p><code>npx</code> allows to execute npm package binaries without having to install them.</p></div></div>\n<p>Then we jump in the newly created folder and install PhoneGap NFC and its types for TypeScript:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token builtin class-name\">cd</span><span class=\"token plain\"> react-nfc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i phonegap-nfc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i --save-dev @types/phonegap-nfc</span><br></span></code></pre></div></div>\n<p>The PhoneGap NFC stuff will be accessible globally, so we don't need any imports in our code.</p>\n<p>Now we open <code>src/App.tsx</code> and we replace its content by the following code, which is going to initialise NFC, invite the user to open the phone settings if NFC is not enabled, and read the content of an NFC tag (PhoneGap NFC offers plenty of other features documented <a href=\"https://github.com/chariotsolutions/phonegap-nfc\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>):</p>\n<div class=\"language-tsx codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">src/App.tsx</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-tsx codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">React</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"react\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"./App.css\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">/** Type for the possible steps of the app */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">type</span><span class=\"token plain\"> </span><span class=\"token class-name\">TStep</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"initializing\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"noNfc\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"nfcNotEnabled\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"waitingForNfcEnabled\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"waitingForTag\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"cancelled\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token operator\" style=\"color:#393A34\">|</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"tagRead\"</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">App</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method-variable function-variable method function property-access maybe-class-name\" style=\"color:#d73a49\">FC</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">step</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> setStep</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">useState</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token maybe-class-name\">TStep</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"initializing\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">tagContent</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> setTagContent</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">useState</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Initialize NFC when the app is started</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">useEffect</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">initializeNfc</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">initializeNfc</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// If nfc is undefined, NFC is not available on this device, or</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// the app is running in a web browser</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> nfc </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"undefined\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Register an event listener</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\tnfc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">addNdefListener</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\tonNdefEvent</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// The callback function for the event listener</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">setStep</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"waitingForTag\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Success → We're waiting for an event</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">setStep</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"nfcNotEnabled\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Error → NFC must not be enabled</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">else</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token function\" style=\"color:#d73a49\">setStep</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"noNfc\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">onGoToSettingsClick</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> nfc </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"undefined\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Ask the device to open the NFC settings for the user</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\tnfc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">showSettings</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">setStep</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"waitingForNfcEnabled\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">alert</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"An error occurred while trying to open the NFC Settings.\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">onNdefEvent</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">e</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">PhoneGapNfc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access maybe-class-name\">TagEvent</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Unregister the event listener</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\tnfc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">removeNdefListener</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">onNdefEvent</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token function\" style=\"color:#d73a49\">setTagContent</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Retrieve the payload of the tag and decode it</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// https://www.oreilly.com/library/view/beginning-nfc/9781449324094/ch04.html</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\tndef</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">textHelper</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">decodePayload</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">e </span><span class=\"token keyword\" style=\"color:#00009f\">as</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">PhoneGapNfc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access maybe-class-name\">NdefTagEvent</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">tag</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">ndefMessage</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">payload</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token function\" style=\"color:#d73a49\">setStep</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"tagRead\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">onStopClick</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token keyword\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token keyword\" style=\"color:#00009f\">typeof</span><span class=\"token plain\"> nfc </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"undefined\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Unregister the event listener</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\tnfc</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">removeNdefListener</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">onNdefEvent</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token function\" style=\"color:#d73a49\">setStep</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"cancelled\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token keyword\" style=\"color:#00009f\">return</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">className</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">nfc</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\">step </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"initializing\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Initializing...</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> step </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"noNfc\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\tThe device you are using doesn't appear to have NFC; or, the</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\tPhoneGap-NFC plugin hasn't been set up correctly.</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> step </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"nfcNotEnabled\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t\tNFC is not enabled on your device. Click the button bellow to open</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t\tyour device's settings, then activate NFC.</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">onClick</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">onGoToSettingsClick</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Go to NFC Settings</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> step </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"waitingForNfcEnabled\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Please click the button below once you have enabled NFC.</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">onClick</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">initializeNfc</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Initialize NFC Reader</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> step </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"waitingForTag\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Scan a NFC Tag to see its content</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">onClick</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">onStopClick</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Stop NFC Reader</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> step </span><span class=\"token operator\" style=\"color:#393A34\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"tagRead\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">?</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Tag scanned! Here it's content:</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\">tagContent</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">onClick</span><span class=\"token tag script language-javascript script-punctuation punctuation\" style=\"color:#393A34\">=</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token tag script language-javascript\" style=\"color:#00009f\">onStopClick</span><span class=\"token tag script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Stop NFC Reader</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">button</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\">Bye!</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t\t\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain-text\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain-text\">\t\t</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">export</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">default</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">App</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>We add some styling in <code>App.css</code>:</p>\n<div class=\"language-css codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockTitle_OeMC\">App.css</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-css codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token selector class\" style=\"color:#00009f\">.nfc</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">height</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">100</span><span class=\"token unit\">vh</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">width</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">100</span><span class=\"token unit\">vw</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">background-color</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token hexcode color\">#21252b</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">color</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token hexcode color\">#abb2bf</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">display</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> flex</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">flex-direction</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> column</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">justify-content</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> center</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">text-align</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> center</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token selector class\" style=\"color:#00009f\">.nfc</span><span class=\"token selector\" style=\"color:#00009f\"> div</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">padding</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">8</span><span class=\"token unit\">px</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">16</span><span class=\"token unit\">px</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token selector\" style=\"color:#00009f\">button</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">margin</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">16</span><span class=\"token unit\">px</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">border</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> none</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">border-radius</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">3</span><span class=\"token unit\">px</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">padding</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">8</span><span class=\"token unit\">px</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">12</span><span class=\"token unit\">px</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">background-color</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token hexcode color\">#61afef</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">color</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token hexcode color\">#21252b</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token property\" style=\"color:#36acaa\">cursor</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> pointer</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></span></code></pre></div></div>\n<p>We can now build the project:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> run build</span><br></span></code></pre></div></div>\n<p>And that's it for our app. The full project can be found <a href=\"https://github.com/Zwyx/react-nfc\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">here</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"mobile-wrappers\">Mobile Wrappers<a href=\"https://zwyx.dev/blog/react-nfc#mobile-wrappers\" class=\"hash-link\" aria-label=\"Direct link to Mobile Wrappers\" title=\"Direct link to Mobile Wrappers\" translate=\"no\">​</a></h2>\n<p>Now, we are going to wrap our web app to create an Android one. Once with Capacitor, once with Cordova (this is twice the same thing, we only do it to show case the two solutions).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"preparing-the-android-device\">Preparing the Android device<a href=\"https://zwyx.dev/blog/react-nfc#preparing-the-android-device\" class=\"hash-link\" aria-label=\"Direct link to Preparing the Android device\" title=\"Direct link to Preparing the Android device\" translate=\"no\">​</a></h3>\n<ul>\n<li class=\"\">Activate the Developer Mode: open the Android settings, go in <strong>System</strong> → <strong>About device</strong>, then tap <strong>Build number</strong> quickly seven times.</li>\n<li class=\"\">Activate USB Debugging: in <strong>Settings</strong> → <strong>System</strong> → <strong>Developer options</strong>, turn <strong>USB debugging</strong> ON.</li>\n<li class=\"\">Connect the device to the computer.</li>\n<li class=\"\">If a notification <strong>USB charging this device</strong> appears, tap on it and select <strong>Transfer files</strong>.</li>\n<li class=\"\">Answer <strong>OK</strong> when Android asks <strong>Allow USB debugging?</strong>.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"capacitor\">Capacitor<a href=\"https://zwyx.dev/blog/react-nfc#capacitor\" class=\"hash-link\" aria-label=\"Direct link to Capacitor\" title=\"Direct link to Capacitor\" translate=\"no\">​</a></h3>\n<p>We run the following commands to install Capacitor, initialise it for our web app (our built app files are located in the <code>build</code> folder), and add the Android platform to our project:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i @capacitor/core @capacitor/cli</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cap init --web-dir build</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cap </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> android</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>If an error occurs, see the first point in the <a href=\"https://zwyx.dev/blog/react-nfc#notes\" class=\"\"><strong>Notes</strong></a> section below.</p></div></div>\n<p>We can now start Android Studio, open the newly created <code>android</code> folder as a project, wait for the scripts execution to finish, then click <strong>Run</strong> → <strong>Run 'app'</strong> to execute our app on our device.</p>\n<!-- -->\n<div style=\"text-align:center\"><div class=\"imageWrapper_u91s withLegend_q8O0\"><div class=\"frame_zT4L\"><img class=\"image_Y_cJ\" src=\"https://zwyx.dev/assets/images/nfc-unavailable-e22021c93b3afd41473a675479f36c9c.webp\" alt=\"Our web app on an Android device with NFC not enabled\" width=\"80%\"></div><div class=\"legend_xj0V\">Our web app on an Android device with NFC not enabled</div></div></div>\n<p>From now on, if we make changes to our web app, we just need to run the following two commands to rebuild the app and update the Capacitor project:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> run build</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cap </span><span class=\"token function\" style=\"color:#d73a49\">sync</span><br></span></code></pre></div></div>\n<p>(If Android Studio is a bit lost, <strong>Project</strong> → <strong>Rebuild</strong> might help — if it is still lost, closing the project and reopen the <code>android</code> folder should work. Likewise, unplugging and plugging back the device might sometimes help is the Android debugger doesn't recognise it.)</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"resources-with-capacitor\">Resources with Capacitor<a href=\"https://zwyx.dev/blog/react-nfc#resources-with-capacitor\" class=\"hash-link\" aria-label=\"Direct link to Resources with Capacitor\" title=\"Direct link to Resources with Capacitor\" translate=\"no\">​</a></h4>\n<p>The app logo and splash screen are located in the <code>android/app/src/main/res folder</code>.</p>\n<p>To create a new logo, in Android Studio, right click on the <code>res</code> folder then select <strong>New</strong> → <strong>Image Asset</strong>.</p>\n<p>To change the splash screen, generate the images using a tool like <a href=\"https://apetools.webprofusion.com/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">this one</a>, then place them in the <code>res</code> folder.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"cordova\">Cordova<a href=\"https://zwyx.dev/blog/react-nfc#cordova\" class=\"hash-link\" aria-label=\"Direct link to Cordova\" title=\"Direct link to Cordova\" translate=\"no\">​</a></h3>\n<p>Cordova requires a few tweaks in the web app itself.</p>\n<p>First, we need to replace the following line in <code>src/index.tsx</code>:</p>\n<div class=\"language-tsx codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-tsx codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token maybe-class-name\">ReactDOM</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">render</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag class-name\" style=\"color:#00009f\">App</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token dom variable\" style=\"color:#36acaa\">document</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">getElementById</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"root\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>by:</p>\n<div class=\"language-tsx codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-tsx codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token dom variable\" style=\"color:#36acaa\">document</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">addEventListener</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token string\" style=\"color:#e3116c\">\"deviceready\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">ReactDOM</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">render</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag class-name\" style=\"color:#00009f\">App</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token dom variable\" style=\"color:#36acaa\">document</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">getElementById</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">\"root\"</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\t</span><span class=\"token boolean\" style=\"color:#36acaa\">false</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>Then, we need to add the following parameter in <code>package.json</code> — at the same level of <code>name</code>, <code>version</code>, etc.:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token property\" style=\"color:#36acaa\">\"homepage\"</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"./\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><br></span></code></pre></div></div>\n<p>We can now rebuild the app:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> run build</span><br></span></code></pre></div></div>\n<p>Now, we need to set up our environment:</p>\n<ul>\n<li class=\"\">Create the <code>ANDROID_SDK_ROOT</code> variable</li>\n<li class=\"\">Add the <code>bin</code> folder of graddle in the <code>PATH</code></li>\n<li class=\"\">Make sure the graddle executable is actually executable</li>\n</ul>\n<p>Replace the <code>&lt;...&gt;</code> in the following commands before running them:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token builtin class-name\">export</span><span class=\"token plain\"> </span><span class=\"token assign-left variable\" style=\"color:#36acaa\">ANDROID_SDK_ROOT</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">path-to-the-android-sdk</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token builtin class-name\">export</span><span class=\"token plain\"> </span><span class=\"token assign-left variable environment constant\" style=\"color:#36acaa\">PATH</span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token environment constant\" style=\"color:#36acaa\">$PATH</span><span class=\"token plain\">:</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">path-to-android-studio</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">/gradle/gradle-</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">version</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">/bin</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:#d73a49\">chmod</span><span class=\"token plain\"> +x </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">path-to-android-studio</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">/gradle/gradle-</span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\">version</span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">/bin/gradle</span><br></span></code></pre></div></div>\n<p>We can now create our Cordova project:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cordova create Cordova com.example ReactNfc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token builtin class-name\">cd</span><span class=\"token plain\"> Cordova</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cordova plugin </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> phonegap-nfc</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cordova platform </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> android</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>info</div><div class=\"admonitionContent_BuS1\"><p>If an error occurs, see the second point in the <a href=\"https://zwyx.dev/blog/react-nfc#notes\" class=\"\"><strong>Notes</strong></a> section below.</p></div></div>\n<p>We place our web app in the Cordova project by copying the content of the <code>build</code> folder in the <code>Cordova/www</code> folder (after having deleted everything in <code>Cordova/www</code>). We also need to add the following line in <code>index.html</code>:</p>\n<div class=\"language-html codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-html codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">src</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">cordova.js</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token script\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><br></span></code></pre></div></div>\n<p>As <code>index.html</code> is minified, to make it simple, we just paste this line at the end of the file, just before the <code>&lt;/body&gt;&lt;/html&gt;</code> tags. So the file ends by:</p>\n<div class=\"language-html codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-html codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">src</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">cordova.js</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token script\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">body</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">html</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><br></span></code></pre></div></div>\n<p>That's it! Cordova requires a few more tweaks than Capacitor, but we now just have to run:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cordova run</span><br></span></code></pre></div></div>\n<p>to launch our app on our device!</p>\n<hr>\n<p>Hopefully this article has given you the basics to create a hybrid mobile app from a web app and use a native feature with it such as NFC. Happy coding!</p>\n<p>Oh, and if you are going to use a package often, you might want to install it instead of using <code>npx</code>, especially Cordova which can take a while to start. It's recommended to install it locally in the project folder.</p>\n<p>To do so, first create the Cordova project with <code>npx</code> then jump in the <code>Cordova</code> folder and run:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> i cordova</span><br></span></code></pre></div></div>\n<p>Now open the file <code>package.json</code> — still in the Cordova folder — and add the following script in the <code>\"scripts\"</code> section:</p>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token property\" style=\"color:#36acaa\">\"cordova\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"node_modules/cordova/bin/cordova\"</span><br></span></code></pre></div></div>\n<p>You can now run, for instance:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> run cordova run</span><br></span></code></pre></div></div>\n<p>To install Cordova globally, you can run:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token function\" style=\"color:#d73a49\">sudo</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">npm</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">install</span><span class=\"token plain\"> </span><span class=\"token parameter variable\" style=\"color:#36acaa\">-g</span><span class=\"token plain\"> cordova</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"notes\">Notes<a href=\"https://zwyx.dev/blog/react-nfc#notes\" class=\"hash-link\" aria-label=\"Direct link to Notes\" title=\"Direct link to Notes\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">An error occurred when I was initialising Capacitor with the PhoneGap NFC plugin. I had to open the file <code>node_modules/@capacitor/cli/dist/android/update.js</code> and replace line <code>208</code> from:</li>\n</ul>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> pathParts </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">getPathParts</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">configElement</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">$</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">parent</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>to:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> pathParts </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">getPathParts</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">\tconfigElement</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">$</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">parent</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">||</span><span class=\"token plain\"> configElement</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">$</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">target</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>This problem <a href=\"https://github.com/ionic-team/capacitor/pull/1794/files\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">has been fixed</a> already, it will probably be pushed on NPM with the next update of Capacitor.</p>\n<ul>\n<li class=\"\">An error occurred when I was initialising Cordova with the PhoneGap NFC plugin. I had to open the file <code>Cordova/plugins/phonegap-nfc/plugin.xml</code> and comment out the following section (starting at line <code>39</code>):</li>\n</ul>\n<div class=\"language-xml codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-xml codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">edit-config</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">file</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">AndroidManifest.xml</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">target</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">/manifest/uses-sdk</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">mode</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">merge</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">uses-sdk</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name namespace\" style=\"color:#00a4db;opacity:0.7\">android:</span><span class=\"token tag attr-name\" style=\"color:#00a4db\">minSdkVersion</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">19</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">edit-config</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><br></span></code></pre></div></div>\n<p>then delete the folder <code>Cordova/platforms/android</code> and execute again:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">npx cordova platform </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> android</span><br></span></code></pre></div></div>\n<p>This error <a href=\"https://github.com/chariotsolutions/phonegap-nfc/issues/371\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">has been identified</a>.</p>",
            "url": "https://zwyx.dev/blog/react-nfc",
            "title": "NFC on Android with React and TypeScript using Capacitor or Cordova",
            "summary": "Hybrid mobile apps are a great way to add native mobile features to an existing responsive web app.",
            "date_modified": "2019-10-05T00:00:00.000Z",
            "author": {
                "name": "Alex",
                "url": "https://github.com/Zwyx"
            },
            "tags": [
                "nfc",
                "typescript",
                "react",
                "capacitor",
                "cordova"
            ]
        }
    ]
}