'Pass custom prop or data to Next JS Link component

I've started messing around with Next JS, and I've come across my first hurdle. I am creating a page displaying a bunch of podcast episodes and I am displaying a little preview card for each podcast on the homepage. The card component looks something like this:

import React from 'react';
import Link from 'next/link';
import { kebabCase } from 'lodash';
import { format } from 'date-fns';
import TextTruncate from 'react-text-truncate';

import { Episode as EpisodeInterface } from '../../interfaces';

type Props = {
  episode: EpisodeInterface;
};

const Episode: React.FunctionComponent<Props> = ({ episode }) => {
  return (
    <Link
      href="episodes/[id]"
      as={`episodes/${episode.itunes.episode}-${kebabCase(episode.title)}`}
    >
      <div className="group transition duration-500 cursor-pointer rounded-lg overflow-hidden shadow-lg border border-cream-darker bg-surface hover:bg-surface-hover hover:border-surface-hover hover:text-text-hover">
        <div className="px-6 py-6">
          <div className="font-bold font-serif text-3xl mb-2">
            {episode.title}
          </div>

          <div className="transition duration-500 flex justify-between mb-2 text-gray-700 group-hover:text-text-hover">
            <span>Episode {episode.itunes.episode}</span>
            <span>{format(new Date(episode.isoDate), 'd MMMM yyyy')}</span>
          </div>

          <div className="mb-2">
            <TextTruncate line={3} text={episode.contentSnippet} />
          </div>
        </div>
      </div>
    </Link>
  );
};

export default Episode;

Now I want to be able to pass the episode object to the full episode page located at /pages/episodes/[id].tsx that is being linked to via the Link element above, rather than have to refetch and filter all the episodes based upon the name of the route that I've chosen episodes/${episode.itunes.episode}-${kebabCase(episode.title)}.

  • Is it possible to pass the entire episode object to the new view?
  • If not, is it possible to pass some more specific data (e.g. unique id) to the view that will enable me to better identify the episode without cluttering the route with query params?


Solution 1:[1]

Adding to @AmerllicA's answer,

I found a way to pass props to the target page when clicking on a <Link> component using getServerSideProps

Nextjs Link Component documentation https://nextjs.org/docs/api-reference/next/link

Nextjs getServerSideProps documentation https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering

You can pass a custom object to the query option of the href prop

<Link
    href={{
        pathname: "episodes/[id]",
        query: {
            id: episode.itunes.episode,
            title: episode.title
        }
    }}
    as={`episodes/${episode.itunes.episode}-${kebabCase(episode.title)}`}
 >
... button stuff
</Link>

in pages/episodes/[id].js

export async function getServerSideProps = (context) => {
    console.log(context.query) 
    // returns { id: episode.itunes.episode, title: episode.title}
    

    //you can make DB queries using the data in context.query
    return {
        props: { 
           title: context.query.title //pass it to the page props
        }
    }
}

And can see the console.log data in the terminal to confirm the data is passed

Finally, you can use the passed props in the episode screen

const Episode = (props) => {
    return (
        <div>{props.title}</div>
    )
}

I think this would work with getStaticProps as well.

Thank you for the question.

Solution 2:[2]

Actually, due to this link, it is an open issue and NextJS has no proper solution for it. Based on NexJS docs you can just pass query params to the routed component. so I understand this is not a solution, but just right now it can fix your issue:

<Link href={{ pathname: '/about', query: { data: JSON.stringify(episode) } }}>
  <a>About us</a>
</Link>

Then in the routed component get the query from URL and parse it:

const RoutedComponent = () => {
  useEffect(() => {
    const { data } = getQueryParams(window.location.search);
  }, []);
};

Note: the getQueryParams is a simple function that returns all params data after the ? in the URL.

Solution 3:[3]

You can't pass data to next/link component.

Even if you would pass it, you won't be able to access it on server-side when a user visits the page directly or refreshes it.

Solution 4:[4]

You can just create a path using template literals as below

href={`episodes/${episode.itunes.episode}-${kebabCase(episode.title)}`}

this way you can use the id in the router query in context of server methods.

But this might not really the optimal way as you must have to then split the episode and the title. what I'd suggest you to do is use the slug feature of the NextJS router for which you can visit the documentation here or follow the bellow steps.

Step 1: Rename your destination file to [...slug].js ('slug' is the genral term you can use whatever you prefer eg:[...episode].js).

Step 2: Add all the params you wish in the href prop forming a single path as below

href={`episodes/${episode.itunes.episode}/${kebabCase(episode.title)}/${episode.anotherProp}`}

Step 3: Make use of the slug in the page itself which will be returned in an array.

For next/router (using inside the component)

import { useRouter } from 'next/router';

export default function SingleEpisode() {
    const router = useRouter();
    const { slug } = router.query;
    const [episodeNumber, episodeTitle, episodeAnotherProp] = slug;
    // I chose to destructure the array but you can always use indices.

    // Your Business Logic.

    return (
    // Your JSX
    )
}

For server methods (using context in getServerSideProps or getStaticProps)

export default function SingleEpisode({episode}) {
    // Your Component
}

export const getServerSideProps = (context) => {
    const { slug } = context.query
    const [episodeNumber, episodeTitle, episodeAnotherProp] = slug;
    // I chose to destructure the array but you can always use indices.

    // Your Business/API/Database Logic

    return {
        props:{
            episode:episodeData // from your logic above.
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Zakher Masri
Solution 2 AmerllicA
Solution 3 Nikolai Kiselev
Solution 4 Vikrant Shah