<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ahyaemon</title>
    <description>The latest articles on DEV Community by ahyaemon (@ahyaemon).</description>
    <link>https://dev.to/ahyaemon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F932488%2Fe4a5ef69-e464-483e-9c88-97a8f255b059.jpg</url>
      <title>DEV Community: ahyaemon</title>
      <link>https://dev.to/ahyaemon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahyaemon"/>
    <language>en</language>
    <item>
      <title>Can You Clear This Challenge Site Designed for Engineers?</title>
      <dc:creator>ahyaemon</dc:creator>
      <pubDate>Thu, 12 Sep 2024 08:00:30 +0000</pubDate>
      <link>https://dev.to/ahyaemon/can-you-clear-this-challenge-site-designed-for-engineers-5c10</link>
      <guid>https://dev.to/ahyaemon/can-you-clear-this-challenge-site-designed-for-engineers-5c10</guid>
      <description>&lt;p&gt;I’ve created a site called Web Dungeon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web-dungeon.ahyaemon.com/en/" rel="noopener noreferrer"&gt;https://web-dungeon.ahyaemon.com/en/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s a skill-testing site where you descend stairs and find answers on each floor. &lt;/p&gt;

&lt;p&gt;Some floors are designed with the assumption that you'll use developer tools.&lt;/p&gt;

&lt;p&gt;I hope it helps novice engineers or those aspiring to become engineers get familiar with some basic developer tools.&lt;/p&gt;

&lt;p&gt;Veteran engineers, feel free to play around for fun.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tech Stack
&lt;/h1&gt;

&lt;p&gt;This is a simple SPA (Single Page Application) with only a frontend.&lt;/p&gt;

&lt;p&gt;There is no backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Languages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Package Management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pnpm.io" rel="noopener noreferrer"&gt;pnpm&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Feels faster overall for adding libraries and building.&lt;/li&gt;
&lt;li&gt;Alternatives like npm and yarn are available, but I chose pnpm for the best experience.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bundler
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Framework
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I considered Solid as well, but went with React for now.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Router
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tanstack.com/router/latest" rel="noopener noreferrer"&gt;TanStack Router&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;I initially used React Router, but switched to TanStack Router after seeing a 5kB reduction in size post-gzip.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Linter + Formatter
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://biomejs.dev/" rel="noopener noreferrer"&gt;Biome&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Fast&lt;/li&gt;
&lt;li&gt;No need for the hassle of configuring eslint and prettier separately.&lt;/li&gt;
&lt;li&gt;Easy to fix code with check --write.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  CSS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Convenient&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;CloudFlare Pages

&lt;ul&gt;
&lt;li&gt;There are edge servers all over the world.&lt;/li&gt;
&lt;li&gt;Deploying is straightforward by specifying the GitHub repository and setting the build command.&lt;/li&gt;
&lt;li&gt;I used &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;CloudFlare&lt;/a&gt; for the domain, which made deploying to a subdomain easy.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Development
&lt;/h1&gt;

&lt;p&gt;Here are the steps to set up a similar environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://vitejs.dev/guide/" rel="noopener noreferrer"&gt;Creating a Vite Project&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a project
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm create vite

? Select a framework:
❯   React

? Select a variant:
❯   TypeScript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install packages
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Start development server
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://biomejs.dev/guides/getting-started/" rel="noopener noreferrer"&gt;Introducing Biome&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;--save-dev&lt;/span&gt; &lt;span class="nt"&gt;--save-exact&lt;/span&gt; @biomejs/biome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create configuration file
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm biome init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates a configuration file (biome.json).&lt;/p&gt;

&lt;p&gt;I prefer to minimize unnecessary semicolons, so I modified the configuration as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;{
    "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
    "organizeImports": {
        "enabled": true
    },
    "linter": {
        "enabled": true,
        "rules": {
            "recommended": true,
            "a11y": {
                "useKeyWithClickEvents": "off"
            }
        }
&lt;span class="gd"&gt;-   }
&lt;/span&gt;&lt;span class="gi"&gt;+   },
+   "javascript": {
+       "formatter": {
+           "semicolons": "asNeeded"
+       }
+   }
&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://tailwindcss.com/docs/guides/vite" rel="noopener noreferrer"&gt;Introducing Tailwind&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss postcss autoprefixer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create configuration file
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpx tailwindcss init &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates tailwind.config.js and postcss.config.js. Add the following to tailwind.config.js:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Modify index.css
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;a href="https://tanstack.com/router/latest/docs/framework/react/installation" rel="noopener noreferrer"&gt;Introducing TanStack Router&lt;/a&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add @tanstack/react-router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these settings, your development environment is set up.&lt;/p&gt;

&lt;h1&gt;
  
  
  About the Answer Logic
&lt;/h1&gt;

&lt;p&gt;If answers are written in JS, they can be easily discovered by looking at the JS files.&lt;/p&gt;

&lt;p&gt;Since there is no backend in this case, hiding answers on the backend is not an option.&lt;/p&gt;

&lt;p&gt;Encryption or hashing answers and embedding them in the JS code is possible, but I decided to dynamically generate answers on the frontend.&lt;/p&gt;

&lt;p&gt;The answers are generated at the time React components are created, making it impossible to share the answers with others.&lt;/p&gt;

&lt;p&gt;By the way, the method for creating answers involves combining two words separated by _ and including random uppercase and lowercase letters to prevent brute-force attacks.&lt;/p&gt;

&lt;p&gt;For example, BlAcK_SpideR.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Feel free to take the challenge!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web-dungeon.ahyaemon.com/en/" rel="noopener noreferrer"&gt;https://web-dungeon.ahyaemon.com/en/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Notes
&lt;/h3&gt;

&lt;p&gt;Here’s an exceptionally clean commit history you might expect in personal development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5c069e0 update
6deec1f update
89f267a update
fd8ff6f update
6c2b180 update
97c2caf update
ed751a6 update
1798d2f update
7002819 update
fdbb774 update
3435c3a update
2d7c9ce update
5c119e8 update
cb5eb44 update
adfeb5e update
8e48a88 update
eb41a5e update
15fa18f update
37f0623 update
9f8bc3f update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>react</category>
      <category>tailwindcss</category>
      <category>challenge</category>
    </item>
    <item>
      <title>Creating a Remix Server to Return a React Page as a PDF</title>
      <dc:creator>ahyaemon</dc:creator>
      <pubDate>Sun, 25 Aug 2024 11:50:22 +0000</pubDate>
      <link>https://dev.to/ahyaemon/creating-a-remix-server-to-return-a-react-page-as-a-pdf-4gd0</link>
      <guid>https://dev.to/ahyaemon/creating-a-remix-server-to-return-a-react-page-as-a-pdf-4gd0</guid>
      <description>&lt;p&gt;I wanted to create a server that returns a page written in React as a PDF, but I couldn't find a standard method for doing so. Therefore, I tried various approaches and am sharing the one that worked for me. If you know of a better way, I'd appreciate your comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Want to Do
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Write the page to be converted to PDF in React.&lt;/li&gt;
&lt;li&gt;Allow the page created in step 1 to be downloadable as a PDF.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server will work as follows: access to / will display the page from step 1, and access to /pdf will allow downloading the page as a PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps and Explanation
&lt;/h2&gt;

&lt;p&gt;Here's a step-by-step guide, starting from project creation.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Creating a Remix Server
&lt;/h3&gt;

&lt;p&gt;First, create the project using the command from &lt;a href="https://remix.run/docs/en/main/start/quickstart" rel="noopener noreferrer"&gt;Remix's official Quick Start guide&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-remix@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the project in an interactive format. I opted to use &lt;code&gt;pnpm&lt;/code&gt; instead of &lt;code&gt;npm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Install dependencies with npm?
&amp;gt; No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the project is created, install the necessary packages:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Next, start the development server:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, if you access &lt;code&gt;http://localhost:5173&lt;/code&gt;, you should see the initial page.&lt;/p&gt;

&lt;p&gt;To ensure that the build passes and the production server can start, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm build
pnpm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can confirm the operation by accessing &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configuration Adjustments
&lt;/h3&gt;

&lt;p&gt;I wanted the development server to run on port 3000, so I modified the &lt;code&gt;scripts&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  "scripts": {
    "build": "remix vite:build",
&lt;span class="gd"&gt;-    "dev": "remix vite:dev",
&lt;/span&gt;&lt;span class="gi"&gt;+    "dev": "remix vite:dev --port 3000",
&lt;/span&gt;    "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
    "start": "remix-serve ./build/server/index.js",
    "typecheck": "tsc"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this change, &lt;code&gt;pnpm dev&lt;/code&gt; will allow access via &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Creating the PDF Endpoint
&lt;/h3&gt;

&lt;p&gt;To make the content of &lt;code&gt;/&lt;/code&gt; available as a PDF at &lt;code&gt;/pdf&lt;/code&gt;, I added &lt;code&gt;puppeteer&lt;/code&gt; to the dependencies to convert HTML to PDF:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm add puppeteer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I added &lt;code&gt;/app/routes/pdf.tsx&lt;/code&gt;. By placing it under the &lt;code&gt;routes&lt;/code&gt; directory, Remix automatically detects it and creates the &lt;code&gt;/pdf&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Disposition&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attachment; filename=remix.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you access &lt;code&gt;http://localhost:3000/pdf&lt;/code&gt; on either the development or production server, the same content as &lt;code&gt;http://localhost:3000&lt;/code&gt; will be downloaded as a PDF.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Font Configuration
&lt;/h3&gt;

&lt;p&gt;To use custom fonts (e.g., &lt;code&gt;NotoSerifJP.ttf&lt;/code&gt;), place the font file in the /public directory, which is automatically created by &lt;code&gt;npx create-remix@latest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, create a CSS file to configure the font and place it under &lt;code&gt;/app&lt;/code&gt;. Here’s the content of &lt;code&gt;/app/base.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'NotoSerifJP'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('/NotoSerifJP.ttf')&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'truetype'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'NotoSerifJP'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, import the CSS in &lt;code&gt;/app/root.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;import "./tailwind.css";
&lt;/span&gt;&lt;span class="gi"&gt;+ import "./base.css";
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now, you can check the page display at &lt;code&gt;http://localhost:3000&lt;/code&gt; and download the PDF at &lt;code&gt;http://localhost:3000/pdf&lt;/code&gt;. By the way, I also tried &lt;a href="https://react-pdf.org/" rel="noopener noreferrer"&gt;React-pdf&lt;/a&gt;, but since it required building the PDF from scratch with limited styling options, I decided to go with the current method.&lt;/p&gt;

</description>
      <category>react</category>
      <category>remix</category>
      <category>programming</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
