Author: Joy kumar
How to Create Dynamic Links Like Meta Tags in Remix!

Create Dynamic Link function like meta function in remix

As we know, we can create a link function in a component, but it only contains static links, not dynamic ones. To create a dynamic <link> tag, we need to understand a hook in Remix and how the meta function receives data in its function parameter.

import { json } from '@remix-run/node'

export const meta = ({ data }) => {
  const { article } = data

  return [
    { title: `${article.title}` },
    {
      name: 'description',
      content: `Explore the insights and perspectives shared by ${article.username}. Stay informed and updated.`,
    },
  ]
}

// Loader function to fetch post data
export async function loader({ params }) {
  const { postid } = params
  console.log(postid)

  if (!postid) {
    throw new Response('Id not found', { status: 404 })
  }
  if (postid == 'favicon.ico') return

  const article = await service.getPost(postid)

  if (!article) {
    throw new Response('article not found', { status: 404 })
  }
  return json({ article })
}

As i research on this topic i find that it internally uses a hook but may be i was wrong anyway the perpose is fullfilled.

So there is a hook useMatches() that takes data from loader from active routes and there now data is passed from meta function to root.jsx file.

import {Meta} from '@remix-run/react'

export function Layout({ children }) {
  return (
    <html lang="en" className="dark bg-black">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
      </head>
      <body className="bg-black">
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  )
}

How to create dynamic links function like meta function

Create dynamic links is mainly used to use loader data in link function so we create a component DynamicLinks.

import * as React from 'react'
import { useMatches } from '@remix-run/react'

export const DynamicLinks = () => {
  const matches = useMatches() //takes loader data from active routes

  let links = []

// loop the data 
  for (const match of matches) {
    if (typeof match.handle?.dynamicLinks === 'function') {
      const routeLinks = match.handle.dynamicLinks({
        data: match.data || {},
      })

      if (Array.isArray(routeLinks)) {
        links.push(...routeLinks)
      } else {
        console.error(
          `dynamicLinks for route ${match.id} must return an array.`
        )
      }
    }
  }

  return (
    <>
      {links.map((link, index) => (
        <link key={index} {...link} />
      ))}
    </>
  )
}

There we use in route components like.

import { json } from '@remix-run/node'

// Loader function to fetch post data
export async function loader({ params }) {
  const { postid } = params

  if (!postid) {
    throw new Response('Id not found', { status: 404 })
  }
  if (postid == 'favicon.ico') return

  const article = await service.getPost(postid)

  if (!article) {
    throw new Response('article not found', { status: 404 })
  }
  return json({ article })
}

// for dynamic links
export const handle = {
  dynamicLinks: ({ data }) => {
    const { article } = data

    if (!article) return []

    return [
      { rel: 'canonical', href: `https://www.wesite.com/post/${article.$id}` },
    ]
  },
}

Then use in root.jsx file link links tag.

import { DynamicLinks } from './components/DynamicLinks.jsx'
import { Links, Meta } from '@remix-run/react'

// Layout component for consistent structure
export function Layout({ children }) {
  return (
    <html lang="en" className="dark bg-black">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
        <DynamicLinks /> 
        />
      </head>
      <body className="bg-black">
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  )
}