1

I am working on an application where users can create posts and can mention other users and use the #hashtag in the post. Through API call I get the list of post, now the question is how can we wrap hashtags and usernames with next.js Link component so that on clicking on these link user can be navigated to different pages without page refresh.

I am able to wrap this with <a> tag and render the text with dangerouslySetInnerHTML. But this way my page will get refreshed by clicking on the link I don't want off course this behavior.

Here's my code to format the text with <a> tag:

export const formatPostText = (text) => {
    let splitText = text.split(' ')
    splitText.forEach((word, index) => {
        if (word[0] === "@") {
            splitText[index] = '<a class="text-cyanBlue" href="/' + word.replace('@', '') + '/in' + '">' + word + '</a>'
        } else if (word[0] === "#") {
            splitText[index] = '<a class="text-cyanBlue" href="hashtag' + '/' + word.replace('#', '') + '">' + word + '</a>'
        }
    })
    return splitText.join(' ')
}

I do want the same behavior as LinkedIn and Facebook has done it.

Approach: One way I can think of is to use React.createElement but I doubt this is gonna work.

Thanks in advance!!

Anurag Tripathi
  • 784
  • 1
  • 13
  • 13
  • 1
    Why would you use string instead of jsx? – Konrad Sep 22 '22 at 11:59
  • User-created posts would be in string format and are stored as strings in the database. for e.g: "Hello everyone, me and @ritika are going to be married this month #happy, #marriage", through API call I get the list of posts with text, now I want to show the text as is, the only difference will be hashtag and username would be wrapped with next js – Anurag Tripathi Sep 22 '22 at 12:20

2 Answers2

1

I used spans with colors here to make example clear: https://codesandbox.io/s/modest-http-wnp5xq?file=/src/App.js

export default function App() {
  const string =
    "Hello everyone, me and @ritika are going to be married this month #happy, #marriage";

  const getJSX = () => {
    // split with capture
    const parts = string.split(/((?:#|@)[a-zA-Z]+)/).filter((s) => s.length);
    return parts.map((part) => {
      if (part.startsWith("@")) {
        // if part starts with `@` return `Link` in your case
        return <span style={{ color: "red" }}>{part}</span>;
      } else if (part.startsWith("#")) {
        // if part starts with `#` return other `Link`
        return <span style={{ color: "green" }}>{part}</span>;
      } else {
        // just return string as is
        return part;
      }
    });
  };

  return <div className="App">{getJSX()}</div>;
}
Konrad
  • 21,590
  • 4
  • 28
  • 64
0

I implemented similar things in a recent project. I hope this might be helpful.

export const formatText = (text) => {
    console.log("text", text)
    let content = text.split(/((?:#|@|https?:\/\/[^\s]+)[a-zA-Z]+)/);
    let hashtag;
    let username;
    return content.map((word) => {
        if (word.startsWith("#")) {
            hashtag = word.replace('#', '')
            return <Link href={`/hashtag/${hashtag}`}><a
                className="text-cyanBlue/80 hover:text-cyanBlue">{word}</a></Link>;
        } else if (word.startsWith("@")) {
            username = word.replace('@', '')
            return <Link href={`/profile/${username}`}><a
                className="text-cyanBlue/80 hover:text-cyanBlue">{word}</a></Link>;
        } else if (word.includes("http")) {
            return <a target="_blank" href={word} className="text-cyanBlue/80 hover:text-cyanBlue">{word}</a>
        } else {
            return word;
        }
    });
}