ブログでよく見かける、カード型のリンクです。
このブログは Markdown で書いているのですが、
URL を貼ると、自動で変換されます。
テキストのリンクよりおしゃれですよね。
例えば ↓ こんなやつです。
https://reactnative.dev/
WordPress なんかだとプラグインがあるようですが、Next.js だと中々たいへんでした。
リンク先の meta を読み込み、title・description・image を表示します。
ビルド時にブログカード表示用の情報を取得する必要があり、
getStaticProps で処理を行います。
[slug].tsximport ReactMarkdown from 'react-markdown'; import BlogCard from '@components/features/blog_card/BlogCard'; import CodeBlock from '@components/codeblock/CodeBlock'; import matter from 'gray-matter'; const jsdom = require('jsdom'); const { JSDOM } = jsdom; export const getStaticProps: ({ params, }:{ params: { slug: string }; }) => = async ({ params }) => { const file = fs.readFileSync(`src/posts/${params.slug}.md`, 'utf-8'); const { data, content } = matter(file); const lines = content.split('\n'); const links: string[] | never = []; // URLの取得 lines.map((line) => { if (line.indexOf('http://') === 0) links.push(line); if (line.indexOf('https://') === 0) links.push(line); }); let cardData = []; const temps = await Promise.all( links.map(async (link) => { const metas = await fetch(link) .then((res) => res.text()) .then((text) => { const metaData = { url: link, title: '', description: '', image: '', }; const doms = new JSDOM(text); const metas: any = doms.window.document.getElementsByTagName('meta'); // title, description, imageを配列にする for (let i = 0; i < metas.length; i++) { let pro = metas[i].getAttribute('property'); if (typeof pro == 'string') { if (pro.match('title')) metaData.title = metas[i].getAttribute('content'); if (pro.match('description')) metaData.description = metas[i].getAttribute('content'); if (pro.match('image')) metaData.image = metas[i].getAttribute('content'); } pro = metas[i].getAttribute('name'); if (typeof pro == 'string') { if (pro.match('title')) metaData.title = metas[i].getAttribute('content'); if (pro.match('description')) metaData.description = metas[i].getAttribute('content'); if (pro.match('image')) metaData.image = metas[i].getAttribute('content'); } } return metaData; }) .catch((e) => { console.error('error', e); }); return metas; }), ); cardData = temps.filter((temp) => temp !== undefined); return { props: { frontMatter: data, content, cardData } }; }; const Article: FC<Props> = ({ frontMatter, content, cardData }) => { return ( <ReactMarkdown components={{ code: CodeBlock, a: BlogCard }}//ここでBlogCardを呼んでいます! className={stylesMarkdown.content} /> ) };
children として Props を渡せないので、Recoil で state を渡しています。
詳細のコードは下記をご参照下さい。
https://github.com/Akihide-Tsue/tsue_sandbox/blob/main/src/components/blog_card/BlogCard.tsx
faviconを取得する関数はこちら。
utils/getFaviconUrl.tsexport function getFaviconUrl(pageUrl: string, size: 32 | 64 = 64) { return `http://www.google.com/s2/favicons?domain=${encodeURIComponent(pageUrl)}&size=${size}`; }
また、BlogCard に表示する meta 情報のスタイルについて、
2 行以上だと...で text を省略する css は下記の通りなのですが、
stylelint が自動で
display: -webkit-box; → display: box;
と書き換えており、悩みました。
/* stylelint-disable-next-line */
にてとりあえず解決(保留)。
stylelintについても研究したい。
BlogCard.module.scss.meta_title { /* stylelint-disable-next-line */ display: -webkit-box; overflow: hidden; text-overflow: ellipsis; word-break: break-all; -webkit-box-orient: vertical; -webkit-line-clamp: $lines; }
参考記事