{"type_of":"article","id":1464479,"title":"Building my portfolio blog with Dev.to and Next.js","description":"Check out my website here: stevenborrie.com  Previously I had experimented with tools like nextra,...","readable_publish_date":"May 11 '23","slug":"building-my-portfolio-blog-with-devto-and-nextjs-268f","path":"/saborrie/building-my-portfolio-blog-with-devto-and-nextjs-268f","url":"https://dev.to/saborrie/building-my-portfolio-blog-with-devto-and-nextjs-268f","comments_count":0,"public_reactions_count":1,"collection_id":null,"published_timestamp":"2023-05-11T07:47:46Z","language":null,"subforem_id":null,"positive_reactions_count":1,"cover_image":null,"social_image":"https://media2.dev.to/dynamic/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkt5qnq9393xk6n6w31m.png","canonical_url":"https://dev.to/saborrie/building-my-portfolio-blog-with-devto-and-nextjs-268f","created_at":"2023-05-11T07:38:41Z","edited_at":"2023-05-11T07:48:55Z","crossposted_at":null,"published_at":"2023-05-11T07:47:46Z","last_comment_at":"2023-05-11T07:47:46Z","reading_time_minutes":2,"tag_list":"","tags":[],"body_html":"\u003cp\u003eCheck out my website here: \u003ca href=\"https://stevenborrie.com\"\u003estevenborrie.com\u003c/a\u003e\u003c/p\u003e\n\n\u003cp\u003ePreviously I had experimented with tools like \u003ca href=\"https://nextra.site/\"\u003enextra\u003c/a\u003e, however I was unhappy with the lack of simplicity, and the default themes.\u003c/p\u003e\n\n\u003ch2\u003e\n  \u003ca name=\"devto-api\" href=\"#devto-api\"\u003e\n  \u003c/a\u003e\n  Dev.to API\n\u003c/h2\u003e\n\n\u003cp\u003eTurns out, the dev.to API is incredibly simple. You can give it a try: \u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003eJSON array of all my posts: \u003ca href=\"https://dev.to/api/articles?username=saborrie\"\u003ehttps://dev.to/api/articles?username=saborrie\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003eThis post: \u003ca href=\"https://dev.to/api/articles/saborrie/building-my-portfolio-blog-with-devto-and-nextjs-268f\"\u003ehttps://dev.to/api/articles/saborrie/building-my-portfolio-blog-with-devto-and-nextjs-268f\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eI realised I can just put all my posts on dev.to, and use it as a CMS database for my website.\u003c/p\u003e\n\n\u003ch2\u003e\n  \u003ca name=\"building-my-blog-in-nextjs\" href=\"#building-my-blog-in-nextjs\"\u003e\n  \u003c/a\u003e\n  Building my blog in Next.js\n\u003c/h2\u003e\n\n\u003cp\u003eAll I wanted to create was 2 pages: a home page, and a blog post page. The home page would be at \u003ccode\u003e/\u003c/code\u003e and the blog page at \u003ccode\u003e/post/[slug]\u003c/code\u003e. On the home page, I wanted to show a list of links to all of my blog posts, and on the blog post page, I wanted to show the content based on the \u003ccode\u003e[slug]\u003c/code\u003e parameter in the URL.\u003c/p\u003e\n\n\u003cp\u003eAfter setting up my project using \u003ccode\u003eyarn create next-app\u003c/code\u003e selecting typescript and the new app router, I added the following code to the homepage in \u003ccode\u003esrc/app/page.tsx\u003c/code\u003e:\u003cbr\u003e\n\u003c/p\u003e\n\n\u003cdiv class=\"highlight js-code-highlight\"\u003e\n\u003cpre class=\"highlight tsx\"\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eLink\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003enext/link\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003eHome\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eposts\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nf\"\u003efetch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003ehttps://dev.to/api/articles?username=saborrie\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003enext\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003erevalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}).\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"nx\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003ereturn \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eh2\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003ePosts\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003eh2\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003eposts\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e(({\u003c/span\u003e \u003cspan class=\"nx\"\u003eslug\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003edescription\u003c/span\u003e \u003cspan class=\"p\"\u003e}:\u003c/span\u003e \u003cspan class=\"kr\"\u003eany\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eLink\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e`/post/\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eslug\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e \u003cspan class=\"na\"\u003eclassName\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"post-list-item\"\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eh3\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003etitle\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003eh3\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003esmall\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003edescription\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003esmall\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nc\"\u003eLink\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cdiv class=\"highlight__panel js-actions-panel\"\u003e\n\u003cdiv class=\"highlight__panel-action js-fullscreen-code-action\"\u003e\n    \u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"\u003e\u003ctitle\u003eEnter fullscreen mode\u003c/title\u003e\n    \u003cpath d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\u003e\u003c/path\u003e\n\u003c/svg\u003e\n\n    \u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"\u003e\u003ctitle\u003eExit fullscreen mode\u003c/title\u003e\n    \u003cpath d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\u003e\u003c/path\u003e\n\u003c/svg\u003e\n\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\n\n\u003cp\u003eThe new app router supports server side fetching by making your page components an \u003ccode\u003easync\u003c/code\u003e function. By default, Next.js will cache the results of the \u003ccode\u003efetch\u003c/code\u003e at build time. We can configure the \u003ccode\u003efetch\u003c/code\u003e to rerun during server rendering by setting the \u003ccode\u003erevalidate\u003c/code\u003e setting - I've set it to revalidate every 10 seconds in the code above.\u003c/p\u003e\n\n\u003cp\u003eTo create the blog post page at \u003ccode\u003e/post/[slug]\u003c/code\u003e I added the file \u003ccode\u003esrc/app/post/[slug]/page.tsx\u003c/code\u003e. Using the following code I am able to write out the title, cover_image, url, and body_html from the dev.to API:\u003cbr\u003e\n\u003c/p\u003e\n\n\u003cdiv class=\"highlight js-code-highlight\"\u003e\n\u003cpre class=\"highlight tsx\"\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003ePost\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"nx\"\u003eparams\u003c/span\u003e \u003cspan class=\"p\"\u003e}:\u003c/span\u003e \u003cspan class=\"kr\"\u003eany\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003epost\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nf\"\u003efetch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`https://dev.to/api/articles/francescoxx/\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eslug\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003enext\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003erevalidate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}).\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"nx\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e());\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003ereturn \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ediv\u003c/span\u003e \u003cspan class=\"na\"\u003eclassName\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"post\"\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eimg\u003c/span\u003e \u003cspan class=\"na\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"cover image\"\u003c/span\u003e \u003cspan class=\"na\"\u003eclassName\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"cover-image\"\u003c/span\u003e \u003cspan class=\"na\"\u003esrc\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003epost\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ecover_image\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eh1\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003epost\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003etitle\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003eh1\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003esmall\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e \u003cspan class=\"na\"\u003ehref\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003epost\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eurl\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003eView on dev.to\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003esmall\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ediv\u003c/span\u003e \u003cspan class=\"na\"\u003edangerouslySetInnerHTML\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003e__html\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nc\"\u003eString\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003epost\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebody_html\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ediv\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003cdiv class=\"highlight__panel js-actions-panel\"\u003e\n\u003cdiv class=\"highlight__panel-action js-fullscreen-code-action\"\u003e\n    \u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"\u003e\u003ctitle\u003eEnter fullscreen mode\u003c/title\u003e\n    \u003cpath d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\u003e\u003c/path\u003e\n\u003c/svg\u003e\n\n    \u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"\u003e\u003ctitle\u003eExit fullscreen mode\u003c/title\u003e\n    \u003cpath d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\u003e\u003c/path\u003e\n\u003c/svg\u003e\n\n\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\n\n\u003cp\u003eThis worked great, except that I needed to do some work to get syntax highlighting in the code snippets. As dev.to is open source you can just go get the scss file at \u003ca href=\"https://github.com/forem/forem/blob/main/app/assets/stylesheets/components/syntax.scss\"\u003ehttps://github.com/forem/forem/blob/main/app/assets/stylesheets/components/syntax.scss\u003c/a\u003e. To import scss in Next.js you need to run \u003ccode\u003eyarn add sass\u003c/code\u003e. And that's it, it worked. I was then able to copy my existing css from my old nextra portfolio in and I was done.\u003c/p\u003e\n\n","body_markdown":"Check out my website here: [stevenborrie.com](https://stevenborrie.com)\n\nPreviously I had experimented with tools like [nextra](https://nextra.site/), however I was unhappy with the lack of simplicity, and the default themes.\n\n## Dev.to API\n\nTurns out, the dev.to API is incredibly simple. You can give it a try: \n\n- JSON array of all my posts: [https://dev.to/api/articles?username=saborrie](https://dev.to/api/articles?username=saborrie)\n- This post: [https://dev.to/api/articles/saborrie/building-my-portfolio-blog-with-devto-and-nextjs-268f](https://dev.to/api/articles/saborrie/building-my-portfolio-blog-with-devto-and-nextjs-268f)\n\nI realised I can just put all my posts on dev.to, and use it as a CMS database for my website.\n\n## Building my blog in Next.js\n\nAll I wanted to create was 2 pages: a home page, and a blog post page. The home page would be at `/` and the blog page at `/post/[slug]`. On the home page, I wanted to show a list of links to all of my blog posts, and on the blog post page, I wanted to show the content based on the `[slug]` parameter in the URL.\n\nAfter setting up my project using `yarn create next-app` selecting typescript and the new app router, I added the following code to the homepage in `src/app/page.tsx`:\n\n```tsx\nimport Link from \"next/link\";\n\nexport default async function Home() {\n  const posts = await fetch(\"https://dev.to/api/articles?username=saborrie\", {\n    next: { revalidate: 10 },\n  }).then((x) =\u003e x.json());\n\n  return (\n    \u003cmain\u003e\n      \u003ch2\u003ePosts\u003c/h2\u003e\n      {posts.map(({ slug, title, description }: any) =\u003e (\n        \u003cLink href={`/post/${slug}`} className=\"post-list-item\"\u003e\n          \u003ch3\u003e{title}\u003c/h3\u003e\n          \u003csmall\u003e{description}\u003c/small\u003e\n        \u003c/Link\u003e\n      ))}\n    \u003c/main\u003e\n  );\n}\n```\n\nThe new app router supports server side fetching by making your page components an `async` function. By default, Next.js will cache the results of the `fetch` at build time. We can configure the `fetch` to rerun during server rendering by setting the `revalidate` setting - I've set it to revalidate every 10 seconds in the code above.\n\nTo create the blog post page at `/post/[slug]` I added the file `src/app/post/[slug]/page.tsx`. Using the following code I am able to write out the title, cover_image, url, and body_html from the dev.to API:\n\n```tsx\nexport default async function Post({ params }: any) {\n  const post = await fetch(`https://dev.to/api/articles/francescoxx/${params.slug}`, {\n    next: { revalidate: 10 },\n  }).then((x) =\u003e x.json());\n\n  return (\n    \u003cdiv className=\"post\"\u003e\n      \u003cimg title=\"cover image\" className=\"cover-image\" src={post.cover_image} /\u003e\n      \u003cmain\u003e\n        \u003ch1\u003e{post.title}\u003c/h1\u003e\n        \u003csmall\u003e\n          \u003ca href={post.url}\u003eView on dev.to\u003c/a\u003e\n        \u003c/small\u003e\n        \u003cdiv dangerouslySetInnerHTML={{ __html: String(post.body_html) }} /\u003e\n      \u003c/main\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nThis worked great, except that I needed to do some work to get syntax highlighting in the code snippets. As dev.to is open source you can just go get the scss file at [https://github.com/forem/forem/blob/main/app/assets/stylesheets/components/syntax.scss](https://github.com/forem/forem/blob/main/app/assets/stylesheets/components/syntax.scss). To import scss in Next.js you need to run `yarn add sass`. And that's it, it worked. I was then able to copy my existing css from my old nextra portfolio in and I was done.","user":{"name":"Steven Borrie","username":"saborrie","twitter_username":null,"github_username":"saborrie","user_id":1078549,"website_url":"https://stevenborrie.com","profile_image":"https://media2.dev.to/dynamic/image/width=640,height=640,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1078549%2F0126a822-77b3-44ac-bff1-bb7b44d3f21b.jpeg","profile_image_90":"https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1078549%2F0126a822-77b3-44ac-bff1-bb7b44d3f21b.jpeg"}}