<?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: dango0812</title>
    <description>The latest articles on DEV Community by dango0812 (@dango0812).</description>
    <link>https://dev.to/dango0812</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%2F1252468%2Fee0e90fc-1e38-4988-aca9-5e2d88d39a58.jpeg</url>
      <title>DEV Community: dango0812</title>
      <link>https://dev.to/dango0812</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dango0812"/>
    <language>en</language>
    <item>
      <title>How to build a responsive virtual grid with tanStack virtual</title>
      <dc:creator>dango0812</dc:creator>
      <pubDate>Sat, 02 Aug 2025 16:54:20 +0000</pubDate>
      <link>https://dev.to/dango0812/building-a-responsive-virtualized-grid-with-tanstack-virtual-37nn</link>
      <guid>https://dev.to/dango0812/building-a-responsive-virtualized-grid-with-tanstack-virtual-37nn</guid>
      <description>&lt;p&gt;When rendering a large number of items, failing to consider performance can lead to laggy scrolling or even slow down the entire app. To address this issue, virtualization techniques were introduced, and among them, TanStack Virtual stands out for its excellent performance and flexibility.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how to build a responsive grid component with aspect ratio support using TanStack Virtual.&lt;/p&gt;

&lt;p&gt;sample code : &lt;a href="https://github.com/dango0812/tanstack-virtual-grid" rel="noopener noreferrer"&gt;https://github.com/dango0812/tanstack-virtual-grid&lt;/a&gt;&lt;br&gt;
website : &lt;a href="https://tanstack-virtual-grid.vercel.app/" rel="noopener noreferrer"&gt;https://tanstack-virtual-grid.vercel.app/&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Although the official TanStack Virtual website offers many great examples, you probably didn’t find one that supports responsive grid layouts with aspect ratio handling.&lt;br&gt;
website:  &lt;a href="https://tanstack.com/virtual/latest/docs/framework/react/examples/dynamic" rel="noopener noreferrer"&gt;https://tanstack.com/virtual/latest/docs/framework/react/examples/dynamic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also an excellent library built on top of TanStack called virtual-grid/react, but even that doesn’t handle aspect ratio calculations out of the box.&lt;br&gt;
website: &lt;a href="https://www.virtual-grid.com/" rel="noopener noreferrer"&gt;https://www.virtual-grid.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I’ll show you how to build a fully responsive virtualized grid using TanStack Virtual—with support for consistent aspect ratios like 1:1 or 16:9, making it truly adaptable across different screen sizes!&lt;/p&gt;

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

&lt;p&gt;📌 Constants Definition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const BREAKPOINT = {
        xs: 375,
        sm: 640,
        lg: 1024
    };
    const ITEM_RATIO = 16 / 9;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;BREAKPOINT: Similar to media queries, these are breakpoints that determine how the grid layout changes based on screen width.&lt;/p&gt;

&lt;p&gt;ITEM_RATIO: The aspect ratio of each item. On larger screens, it maintains a 16:9 ratio, while on smaller screens, it switches to a square shape.&lt;/p&gt;

&lt;p&gt;📌 Row &amp;amp; Column Virtualization using useVirtualizer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const rowVirtualizer = useVirtualizer({
        count: Math.ceil(items.length / columns),
        getScrollElement: () =&amp;gt; parentRef.current,
        estimateSize: () =&amp;gt; itemSize.height + gap.y,
        overscan: 2
    });

    const columnVirtualizer = useVirtualizer({
        horizontal: true,
        count: columns,
        getScrollElement: () =&amp;gt; parentRef.current,
        estimateSize: () =&amp;gt; itemSize.width + gap.x,
        overscan: 2
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both rows and columns are virtualized separately, so only visible elements are rendered.&lt;/p&gt;

&lt;p&gt;estimateSize: Estimates the height/width of each item to calculate the total virtual scroll area.&lt;br&gt;
overscan: Renders additional items outside the viewport for improved scroll performance.&lt;/p&gt;

&lt;p&gt;📌 useResizeObserver&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { width: containerWidth } = useResizeObserver(parentRef);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uses ResizeObserver to detect changes in the width of the parent container.&lt;br&gt;
In a real project, you might want to create a useWindowSize hook instead, to track window size directly!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleResize = throttle(() =&amp;gt; {
    ...
}, 200);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function is triggered whenever the screen size changes.&lt;br&gt;
It’s throttled for performance, and recalculates gap, column count, and item size, then updates the state.&lt;/p&gt;

&lt;p&gt;📌 Layout Calculation Functions&lt;br&gt;
Each function is used within handleResize and calculates layout based on the current screen width.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getItemGap = (width) =&amp;gt; {...}
const getColumnsCount = (width) =&amp;gt; {...}
const getItemWidth = (width, columns, gapX) =&amp;gt; {...}
const getItemHeight = (width, itemWidth) =&amp;gt; {...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;getItemGap: Adjusts item gap (in px) based on breakpoints.&lt;br&gt;
getColumnsCount: Dynamically changes the number of columns according to width.&lt;br&gt;
getItemWidth: Calculates item width by subtracting total gap from container width.&lt;br&gt;
getItemHeight: Calculates height based on aspect ratio. Uses square ratio on small screens.&lt;/p&gt;

&lt;p&gt;📌 Calling tanstack measure function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    rowVirtualizer.measure();
    columnVirtualizer.measure();
}, [itemSize.height, columns]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When item size or column count changes, re-measures the virtualizer’s internal layout.&lt;/p&gt;

&lt;p&gt;📌 Rendering&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{rowVirtualizer.getVirtualItems().map((virtualRow) =&amp;gt;
    columnVirtualizer.getVirtualItems().map((virtualColumn) =&amp;gt; {
        ...
    })
)}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nested loop structure iterates through both rows and columns.&lt;/p&gt;

&lt;p&gt;Each item’s position is calculated using translateX and translateY (a required technique in virtual scrolling).&lt;/p&gt;

&lt;p&gt;itemIndex is used to determine which item from the data array should be rendered.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to handle a language-less main domain with SEO considerations in a Next.js + i18next environment</title>
      <dc:creator>dango0812</dc:creator>
      <pubDate>Wed, 23 Jul 2025 07:09:21 +0000</pubDate>
      <link>https://dev.to/dango0812/how-to-handle-a-language-less-main-domain-with-seo-considerations-in-a-nextjs-i18next-environment-2e5j</link>
      <guid>https://dev.to/dango0812/how-to-handle-a-language-less-main-domain-with-seo-considerations-in-a-nextjs-i18next-environment-2e5j</guid>
      <description>&lt;p&gt;When running a global service, multilingual support becomes an inevitable challenge.&lt;/p&gt;

&lt;p&gt;Next.js provides built-in i18n features, and when used with i18next, translation management becomes more streamlined.&lt;br&gt;
However, when working on i18n, you often face questions like:&lt;/p&gt;

&lt;p&gt;“Why is the language code added even for the default language?”&lt;br&gt;
“Shouldn't the default language be served without a language prefix?”&lt;br&gt;
“Will this cause any SEO issues?”&lt;/p&gt;

&lt;p&gt;In this article, we'll show you how to use Next.js Middleware to remove the language prefix from the main domain for the default language.&lt;/p&gt;

&lt;p&gt;🔎 Why is the domain important for SEO?&lt;br&gt;
Search engines may recognize multiple URLs with the same content as duplicate content.&lt;br&gt;
For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;domain.com/about
domain.com/ko/about
domain.com/ja/about
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all three URLs serve the same content in the same language, search engines might see them as duplicates.&lt;br&gt;
This can lead to:&lt;/p&gt;

&lt;p&gt;Diluted SEO rankings (because link equity is split)&lt;/p&gt;

&lt;p&gt;Indexing issues (search engines unsure which page to prioritize)&lt;/p&gt;

&lt;p&gt;Reduced crawl efficiency&lt;/p&gt;

&lt;p&gt;To prevent this, it's important to:&lt;/p&gt;

&lt;p&gt;Use proper canonical tags&lt;/p&gt;

&lt;p&gt;Configure hreflang attributes for multilingual versions&lt;/p&gt;

&lt;p&gt;Ensure only one version is accessible for each language when possible&lt;/p&gt;

&lt;p&gt;Managing URLs carefully in a multilingual setup is essential for avoiding SEO penalties and maximizing visibility.&lt;/p&gt;

&lt;p&gt;🛠️ how to set up i18next with Next.js&lt;br&gt;
blog: &lt;a href="https://www.locize.com/blog/i18n-next-app-router" rel="noopener noreferrer"&gt;https://www.locize.com/blog/i18n-next-app-router&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;i18next library middleware code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Language Check

2. Redirect if language in path is not supported
if (!lngInPath &amp;amp;&amp;amp; !req.nextUrl.pathname.startsWith('/_next')) {
    return NextResponse.redirect(new URL(`/${lng}${req.nextUrl.pathname}
${req.nextUrl.search}`, req.url))
  }

3. Continue
NextResponse.next({ ... })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🚀 modified middleware code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Language Check

2. Language Check &amp;amp; Fallback Language Check

3. Handling access to the fallback language (replace fallback language in URL)
Replace the fallback language segment in the target URL:
target = target.replace(`/${fallbackLng}`, target === `/${fallbackLng}` ? "/" : "");

-&amp;gt; Redirect not includes fallback language
NextResponse.redirect(new URL(target, req.url));

-&amp;gt; Rewrite the fallback language page
NextResponse.rewrite(new URL(`/${fallbackLng}${pathname}${search}`, req.url));

4. Continue
NextResponse.next({ ... })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dive deeper on github.&lt;br&gt;
code : &lt;a href="https://github.com/dango0812/tanstack-virtual-grid/blob/main/middleware.ts" rel="noopener noreferrer"&gt;https://github.com/dango0812/tanstack-virtual-grid/blob/main/middleware.ts&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Stop searching through old docs for social share urls — get all the latest, ready-to-use URLs right here.</title>
      <dc:creator>dango0812</dc:creator>
      <pubDate>Wed, 09 Jul 2025 01:56:49 +0000</pubDate>
      <link>https://dev.to/dango0812/social-share-url-made-easy-for-web-developers-2121</link>
      <guid>https://dev.to/dango0812/social-share-url-made-easy-for-web-developers-2121</guid>
      <description>&lt;p&gt;Are you building a blog, portfolio, or product site and want users to share your content on social media?&lt;/p&gt;

&lt;p&gt;Instead of digging through outdated docs or guessing URL formats, I built a simple solution — a collection of ready-to-use social share URLs with corresponding icons (SVG, PNG, JPG).&lt;/p&gt;

&lt;p&gt;Why install a full library when you only need to support two share services?&lt;br&gt;
You don’t need a massive library just to add simple social sharing!&lt;br&gt;
Keep it light skip the heavy libraries and just use simple share URLs.&lt;/p&gt;

&lt;p&gt;You can check it out here.&lt;br&gt;
👉 Github: &lt;a href="https://github.com/dango0812/social-share-urls" rel="noopener noreferrer"&gt;https://github.com/dango0812/social-share-urls&lt;/a&gt;&lt;br&gt;
The details documentation is still a work in progress!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 How to Use&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Example: Share on Twitter
const searchParams = new URLSearchParams({
    url: "https://github.com/dango0812", // share url
    text: "hello, world"
});
const shareUrl = `https://twitter.com/intent/tweet?${searchParams.toString()}`;

&amp;lt;a href={shareUrl} target="_blank" rel="noopener noreferrer"&amp;gt;
    twitter share!
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since each platform has different URL templates, we've organized them all in this project.&lt;br&gt;
You can copy them and use them right away!&lt;/p&gt;

</description>
      <category>social</category>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to add a table of contents in Ghost without editing default.hbs</title>
      <dc:creator>dango0812</dc:creator>
      <pubDate>Mon, 15 Jul 2024 09:09:47 +0000</pubDate>
      <link>https://dev.to/dango0812/how-to-add-a-table-of-contents-in-ghost-without-editing-defaulthbs-53jd</link>
      <guid>https://dev.to/dango0812/how-to-add-a-table-of-contents-in-ghost-without-editing-defaulthbs-53jd</guid>
      <description>&lt;p&gt;Hello, everyone 👋&lt;/p&gt;

&lt;p&gt;Many people have probably accessed the tutorial provided by Ghost on how to add a table of contents, as shown in the link below.&lt;br&gt;
&lt;a href="https://ghost.org/tutorials/adding-table-of-contents" rel="noopener noreferrer"&gt;https://ghost.org/tutorials/adding-table-of-contents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The method provided by Ghost is written for developers, so it is complicated and time-consuming.&lt;br&gt;
In this post, we offer a simple and easy way to create a table of contents that anyone can follow, even if you are not a developer.&lt;/p&gt;

&lt;p&gt;※ The method provided by Ghost applies to all posts, but the method in this post requires manual work.&lt;br&gt;
It is written based on the Ruby theme.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#STEP 1&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F869t1we3rj1dqonqxxxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F869t1we3rj1dqonqxxxd.png" alt="place code at Post header head, foot" width="800" height="269"&gt;&lt;/a&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;lt;script src="https://cdn.jsdelivr.net/npm/tocbot@4.21.0/dist/tocbot.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;link href="https://cdn.jsdelivr.net/npm/tocbot@4.21.0/dist/tocbot.min.css" rel="stylesheet"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;place code at Post header &lt;strong&gt;ghost_head&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#STEP 2&lt;/strong&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;lt;script&amp;gt;
    tocbot.init({
        tocSelector: '.toc',
        linkClass: 'toc-link',
        orderedList: true,
        headingSelector: 'h1, h2, h3',
        collapseDepth: 3,
        contentSelector: '.gh-content',
        ignoreSelector: '.kg-header-card &amp;gt; *',
        headingsOffset: 40,
        scrollSmooth: true,
        scrollSmoothDuration: 420,
        scrollSmoothOffset: -40,
        hasInnerContainers: true
    });
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;place code at Post header &lt;strong&gt;ghost_foot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#STEP 3&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoxkgd7mxkhf2a07sdij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoxkgd7mxkhf2a07sdij.png" alt="Use HTML card to insert the table of content everywhere you like" width="800" height="373"&gt;&lt;/a&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;lt;div class="toc"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;style&amp;gt;
    .toc:before {
        content: "Table of content";
        display: block;
        margin-bottom: 20px;
        font-size: larger;
        font-weight: bold;
        border-bottom: 1px dashed #dadada;
        padding-bottom: 10px;
    }

    .toc {
        padding: 30px;
        border: 1px solid #dadada;
        border-radius: 5px;
        background-color: #fafafa;
    }

    a.toc-link {
        font-size: 80%;
        text-decoration: none;
    }

    li.toc-list-item {
        margin-top: 0;
    }

    .toc-list .is-collapsible {
        margin-left: 15px;
        color: #666;
    }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use HTML card to insert the table of contents everywhere you like&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#STEP 4&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45xdyzjcqdglzg8meibw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45xdyzjcqdglzg8meibw.png" alt="titles using h1, h2, h3 tag" width="742" height="712"&gt;&lt;/a&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;lt;h1 id="title#1"&amp;gt;Title #1&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and, if you create titles using 'h1, h2, h3' with an HTML card, they will be included in the table of contents.&lt;br&gt;
※ You need to input an id to specify what each table of contents link item points to.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;How can I display the table of contents on the left or right side instead of at the top of the post?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F709ki05vn9p6502bdvfl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F709ki05vn9p6502bdvfl.png" alt="table of contents" width="450" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;instead of the STEP 3 HTML card code you added earlier, write the code as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="toc"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;style&amp;gt;
    .toc:before {
        content: "Table of contents";
        display: block;
        font-size: larger;
        font-weight: bold;
        padding-bottom: 20px;
    }

    .toc {
        position: fixed;
        right: 10%;
        top: 50%;
        transform: translateY(-50%);
        padding: 30px;
        border: 1px solid #dadada;
        border-radius: 20px;
        background-color: #fafafa;
        @media (max-width: 1200px) {
            display: none;
        }
    }

    a.toc-link {
        font-size: 80%;
        text-decoration: none;
    }

    li.toc-list-item {
        margin-top: 0;
    }

    .toc-list .is-collapsible {
        margin-left: 15px;
        color: #666;
    }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, proceed to STEP 4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note when inserting the table of contents automatically using TOCBOT&lt;br&gt;
After you have installed TOCBOT, add the following HTML card to any post you want.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An extremely important note is that each theme will not be the same and will have different content classes. contentSelector: '.gh-content'&lt;/p&gt;

&lt;p&gt;You can access the preview or uploaded post and use the browser's 'Developer Tools' to check what values the post is based on.&lt;/p&gt;

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

&lt;p&gt;After clicking the mouse icon highlighted in red, hover over the content of the post to find the content id value used by the theme.&lt;/p&gt;

&lt;p&gt;Then, simply change the value of the contentSelector you entered in ghost_foot, and you're done!&lt;/p&gt;

&lt;p&gt;Thank you for reading it 😁&lt;/p&gt;

&lt;p&gt;reference document: &lt;a href="https://ghostfam.com/en/create-a-table-of-contents-for-ghost" rel="noopener noreferrer"&gt;https://ghostfam.com/en/create-a-table-of-contents-for-ghost&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ghost</category>
      <category>tocbot</category>
      <category>toc</category>
      <category>javascript</category>
    </item>
    <item>
      <title>NextAuth with AWS Cognito Email &amp; Google Sign in</title>
      <dc:creator>dango0812</dc:creator>
      <pubDate>Fri, 26 Jan 2024 16:41:19 +0000</pubDate>
      <link>https://dev.to/dango0812/nextauth-with-aws-cognito-email-google-sign-in-5ef6</link>
      <guid>https://dev.to/dango0812/nextauth-with-aws-cognito-email-google-sign-in-5ef6</guid>
      <description>&lt;p&gt;Hello, everyone 👋&lt;/p&gt;

&lt;p&gt;This document provide information on how to use the Next Auth library to implement email and Google sign in with AWS Cognito.&lt;/p&gt;

&lt;p&gt;Sample code: &lt;a href="https://github.com/dango0812/nextauth-cognito"&gt;https://github.com/dango0812/nextauth-cognito&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Understand content and code written in this document, it is helpful to be familiar with the following.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;AWS Cognito is Service Provider&lt;br&gt;
AWS Cognito does not offer direct support for Single Sign-On (SSO) using SAML 2.0 authentication methods. Therefore, if you intend to implement SSO with SAML 2.0, you must use a service such as Microsoft Azure or Okta (Auth0).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The AWS Cognito Provider in NextAuth provide code written using the OpenID Connect authentication method.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NextAuth Cognito Provider Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function Cognito&amp;lt;P extends CognitoProfile&amp;gt;(
  options: OAuthUserConfig&amp;lt;P&amp;gt;
): OAuthConfig&amp;lt;P&amp;gt; {
  return {
    id: "cognito",
    name: "Cognito",
    wellKnown: `${options.issuer}/.well-known/openid-configuration`,
    idToken: true,
    ...
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that need to input the well-known (openid-configuration) URL related to OpenID Connect.&lt;/p&gt;

&lt;p&gt;To confirm more thoroughly, shall we check the internals of the OAuthConfig?&lt;/p&gt;

&lt;p&gt;NextAuth OAuthConfig Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface OAuthConfig&amp;lt;P&amp;gt; extends CommonProviderOptions, PartialIssuer {
  wellKnown?: string
  authorization?: string | AuthorizationEndpointHandler
  token?: string | TokenEndpointHandler
  type: "oauth"
  checks?: ChecksType | ChecksType[]
  clientId?: string
  idToken?: boolean
  ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;type is oauth and use checks type used in OAuth&lt;/p&gt;

&lt;p&gt;If you understand OpenID Connect, you can ascertain that it supports only OAuth functionality.&lt;/p&gt;

&lt;p&gt;If you've understood the above content, go ahead and proceed with Google Sign-In using the CognitoProvider written below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import CognitoProvider from "next-auth/providers/cognito"

CognitoProvider({
    id: 'cognito',
    name: 'Cognito',
    type: 'oauth',
    idToken: true,
    clientId: '',
    // your aws cognito client id
    clientSecret: null,
    // if your AWS Cognito secret exists, input your secret.
    // if the AWS Cognito secret is blank, input null.
    client: {
        token_endpoint_auth_method: "none"
    },
    checks: ['state', 'nonce'],
    issuer: '',
    // your aws cognito issuer
    wellKnown: 'https://cognito-idp.ap-northeast-2.amazonaws.com/####/.well-known/openid-configuration',
    // input your aws cognito userpool id in ####
    authorization: {
        url: '####/oauth2/authorize',
        // input your aws cognito domain url in ####
        params: {
            response_type: "code",
            client_id: '',
            // your aws cognito client Id
            identity_provider: 'Google',
            scope: 'openid email profile aws.cognito.signin.user.admin',
            redirect_uri: '####/api/auth/callback/cognito'
            // your redirect url
        },
    },
    profile(profile, tokens) {
        return { ...profile, ...tokens }
    }
}),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The redirect_uri should be set to api/auth/callback/cognito. &lt;/p&gt;

&lt;p&gt;NextAuth handle federated token and user api for AWS Cognito OAuth 2.0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If that's the case, how should approach email login?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To implement the SAML 2.0 authentication method, you can either access it through the IdP URL provided by another platform or handle the authentication using NextAuth credentials provider with the AWS Cognito library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CredentialsProvider({
    id: 'credentials',
    name: 'credentials',
    credentials: {
        email: { label: 'email', type: 'text' },
        password: { label: 'password', type: 'password' }
    },
    authorize(credentials) {

        const cognitoUser = new CognitoUser({
            Username: credentials?.email,
            Pool: "",
            // your aws cognito userpool
        });

        const authenticationDetails = new AuthenticationDetails({
            Username: credentials?.email,
            Password: credentials?.password
        });

        return new Promise((resolve, reject) =&amp;gt; {
            cognitoUser.authenticateUser(authenticationDetails, {
                onSuccess: (session) =&amp;gt; {
                    if (session instanceof CognitoUserSession) {
                        const userInfo = {
                            id: session.getIdToken().payload.sub,
                            email: session.getIdToken().payload.email,
                            name: session.getIdToken().payload.name,
                            idToken: session.getIdToken().getJwtToken(),
                            accessToken: session.getAccessToken().getJwtToken(),
                            refreshToken: session.getRefreshToken().getToken(),
                        };

                        resolve(userInfo);
                    }
                },
                onFailure: (error) =&amp;gt; {
                    if (error) {
                        reject(error);
                    }
                }
            })
        })
    },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thank you for reading it 😁&lt;/p&gt;

&lt;p&gt;Sample Code: &lt;br&gt;
&lt;a href="https://github.com/dango0812/nextauth-cognito"&gt;https://github.com/dango0812/nextauth-cognito&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS Cognito Service Provider: &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html"&gt;https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next Auth CognitoProvider Internal Library: &lt;a href="https://github.com/nextauthjs/next-auth/blob/v4/packages/next-auth/src/providers/cognito.ts"&gt;https://github.com/nextauthjs/next-auth/blob/v4/packages/next-auth/src/providers/cognito.ts&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextauth</category>
      <category>cognito</category>
      <category>nextjs</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
