Next.JS - Gist's embedded in markdown rendered via dangerouslySetInnerHTML only show after full page load, not React route load

Asked
Active3 hr before
Viewed126 times

7 Answers

markdownrendereddangerouslysetinnerhtmlembeddedroutereact
90%

It has you use <ReactMarkdown> component to render your markdown (instead of remark) and then you use the react-syntax-highlighter library in your custom code block component.,Find centralized, trusted content and collaborate around the technologies you use most., How do you test and demonstrate that you have properly prevented a race condition? ,Basically, you create a custom code block that formats your code tags inline.

When rendering your markdown use:

import CodeBlock from "../../components/codeblock"

<ReactMarkdown components={CodeBlock}>{your markdown data here}</ReactMarkdown>
load more v
88%

The MD contents are rendered to the web page by way of dangerouslySetInnerHTML, and all is ok when the web page is navigated to straight. Nevertheless when the app’s routing is used and a full web page refresh would not occur the script tag is included within the markup, however not executed.,I’ve a collection of weblog posts saved in MD recordsdata, a few of these include a number of Gist embeds within the type of script tags., Free download Heart Care python django full project , Python Face recognition based attendance system free download

Markdown:

---
title: "Instance of GIST embedding"
date: "2020-02-20"
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut id arcu at arcu pretium porta. Nam feugiat est ut lectus imperdiet venenatis. Ut tempus vitae lectus id vestibulum. Sed tristique est metus. Ut pretium malesuada risus. Maecenas eget diam tristique, sagittis velit ac, efficitur nisi. Quisque lectus lorem, vehicula at mi vitae, dapibus volutpat augue. Sed dignissim pharetra ligula a efficitur. In ultrices imperdiet libero. Quisque ornare erat eu elit ullamcorper faucibus. Maecenas mattis sem a mauris posuere iaculis.

<script src="https://gist.github.com/robearlam/aec15c65aaffbd5ec00a826c5cbe57ad.js"></script>

Etiam sed interdum ligula, nec tincidunt justo. Aliquam erat volutpat. Fusce in scelerisque nisl. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin venenatis lectus at ligula mollis dapibus. Praesent condimentum metus fringilla, commodo enim non, fringilla dui. Vivamus nec ligula lacinia ante semper rhoncus eu sed nisi. In ac dolor vel lorem tincidunt lacinia. Praesent quis mattis mi, at finibus velit. Etiam auctor, magna fermentum tincidunt interdum, nulla augue porttitor enim, ac lobortis felis eros id dui. Suspendisse dignissim, dui sit amet pulvinar iaculis, nisi tellus rhoncus dolor, eu gravida risus massa accumsan magna.
load more v
72%

If you're creating an information-dense website like documentation or a blog, you're probably considering using Markdown. Most developers are familiar with Markdown from GitHub and other online communities.,Metadata allows you to describe the contents of your Markdown file. For example, adding a title and an author of a blog post. If you're using Markdown, gray-matter allows you to write a YAML front-matter like so:,If you're using CSS or Sass, you can target your HTML elements inside of the component containing the Markdown. For example, Tailwind Typography will automatically style the HTML elements generated from your Markdown inside an element with the prose class.,If you're using CSS-in-JS or want more granular control at the component level, another option is next-mdx-remote. This also allows you to use custom components inside your Markdown (e.g. Heading) by providing an object with components for the Markdown to transform to.

Markdown allows you to transform plaintext into formatted elements. For example–you want to write a 2000 character blog post, including rich formatting options like bold text, italicized text, and links. You'd like to optimize for writing and spend less time coding. And let's be honest – writing clearly is more difficult. Rather than writing HTML like this:

<p>
   I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a>
</p>
<p>
   I <strong>love</strong> using <a href="https://nextjs.org/">Next.js</a>
</p>

Instead, you can use Markdown to express your styling:

I ** love ** using[Next.js](https: //nextjs.org/)
I ** love ** using[Next.js](https: //nextjs.org/)

If you're using CSS or Sass, you can target your HTML elements inside of the component containing the Markdown. For example, Tailwind Typography will automatically style the HTML elements generated from your Markdown inside an element with the prose class.

<article class="prose lg:prose-xl">
   <h1>Garlic bread with cheese: What the science tells us</h1>
   <p>
      For years parents have espoused the health benefits of eating garlic bread with cheese to their
      children, with the food earning such an iconic status in our culture that kids will often dress
      up as warm, cheesy loaf for Halloween.
   </p>
   <p>
      But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
      springing up around the country.
   </p>
   <!-- ... -->
</article>
<article class="prose lg:prose-xl">
   <h1>Garlic bread with cheese: What the science tells us</h1>
   <p>
      For years parents have espoused the health benefits of eating garlic bread with cheese to their
      children, with the food earning such an iconic status in our culture that kids will often dress
      up as warm, cheesy loaf for Halloween.
   </p>
   <p>
      But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
      springing up around the country.
   </p>
   <!-- ... -->
</article>

If you're using CSS-in-JS or want more granular control at the component level, another option is next-mdx-remote. This also allows you to use custom components inside your Markdown (e.g. Heading) by providing an object with components for the Markdown to transform to.

// pages/index.js

import renderToString from 'next-mdx-remote/render-to-string'
import hydrate from 'next-mdx-remote/hydrate'

import Heading from '../components/heading'

const components = { Heading }

export default function Post({ source }) {
  const content = hydrate(source, { components })
  return <div className="wrapper">{content}</div>
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source = 'Some **mdx** text, with a component <Heading />'
  const mdxSource = await renderToString(source, { components })
  return { props: { source: mdxSource } }
}
// pages/index.js

import renderToString from 'next-mdx-remote/render-to-string'
import hydrate from 'next-mdx-remote/hydrate'

import Heading from '../components/heading'

const components = { Heading }

export default function Post({ source }) {
  const content = hydrate(source, { components })
  return <div className="wrapper">{content}</div>
}

export async function getStaticProps() {
  // MDX text - can be from a local file, database, anywhere
  const source = 'Some **mdx** text, with a component <Heading />'
  const mdxSource = await renderToString(source, { components })
  return { props: { source: mdxSource } }
}

Or @next/mdx using an MDXProvider.

// pages/index.js

import { MDXProvider } from '@mdx-js/react'
import Image from 'next/image'
import { Heading, Text, Pre, Code, Table } from '../components'

const components = {
  img: Image,
  h1: Heading.H1,
  h2: Heading.H2,
  p: Text,
  code: Pre,
  inlineCode: Code
}

export default function Post(props) {
  return (
    <MDXProvider components={components}>
      <main {...props} />
    </MDXProvider>
  )
}
// pages/index.js

import { MDXProvider } from '@mdx-js/react'
import Image from 'next/image'
import { Heading, Text, Pre, Code, Table } from '../components'

const components = {
  img: Image,
  h1: Heading.H1,
  h2: Heading.H2,
  p: Text,
  code: Pre,
  inlineCode: Code
}

export default function Post(props) {
  return (
    <MDXProvider components={components}>
      <main {...props} />
    </MDXProvider>
  )
}

When using an approach that has dynamic routing (e.g. /pages/doc/[slug].js), you can wrap the returned React component with a shared <Layout />. Since it's just a React component, you could have separate layouts like <BlogLayout /> or <DocsLayout />.

// pages/docs/[slug].js

import Layout from '../components/Layout'
import { getAllDocs, getDocBySlug } from '../lib/docs'
import markdownToHtml from '../lib/markdown'

export default function Doc({ meta, content }) {
  return <Layout meta={meta}>{content}</Layout>
}

export async function getStaticProps({ params }) {
  const doc = getDocBySlug(params.slug)
  const content = await markdownToHtml(doc.content || '')

  return {
    props: {
      ...doc,
      content
    }
  }
}

export async function getStaticPaths() {
  const docs = getAllDocs()

  return {
    paths: docs.map(doc => {
      return {
        params: {
          slug: doc.slug
        }
      }
    }),
    fallback: false
  }
}
// pages/docs/[slug].js

import Layout from '../components/Layout'
import { getAllDocs, getDocBySlug } from '../lib/docs'
import markdownToHtml from '../lib/markdown'

export default function Doc({ meta, content }) {
  return <Layout meta={meta}>{content}</Layout>
}

export async function getStaticProps({ params }) {
  const doc = getDocBySlug(params.slug)
  const content = await markdownToHtml(doc.content || '')

  return {
    props: {
      ...doc,
      content
    }
  }
}

export async function getStaticPaths() {
  const docs = getAllDocs()

  return {
    paths: docs.map(doc => {
      return {
        params: {
          slug: doc.slug
        }
      }
    }),
    fallback: false
  }
}

When using @next/mdx and file-system based routing, you can make your Layout the default export of the file.

// pages/posts/markdown-with-next.mdx

import Layout from '../components/Layout'

export const meta = {
  title: 'Markdown/MDX with Next.js',
  author: 'Lee Robinson'
}

I **love** using [Next.js](https://nextjs.org/)

export default ({ children }) => <Layout meta={meta}>{children}</Layout>
// pages/posts/markdown-with-next.mdx

import Layout from '../components/Layout'

export const meta = {
  title: 'Markdown/MDX with Next.js',
  author: 'Lee Robinson'
}

I **love** using [Next.js](https://nextjs.org/)

export default ({ children }) => <Layout meta={meta}>{children}</Layout>

Metadata allows you to describe the contents of your Markdown file. For example, adding a title and an author of a blog post. If you're using Markdown, gray-matter allows you to write a YAML front-matter like so:

-- -
title: Markdown / MDX with Next.js
author: Lee Robinson
   -- -

   I ** love ** using[Next.js](https: //nextjs.org/)
-- -
title: Markdown / MDX with Next.js
author: Lee Robinson
   -- -

   I ** love ** using[Next.js](https: //nextjs.org/)

Which can then be transformed into a JS object when reading your files.

import matter from 'gray-matter'

const docsDirectory = join(process.cwd(), 'docs')

export function getDocBySlug(slug) {
   const realSlug = slug.replace(/\.md$/, '')
   const fullPath = join(docsDirectory, `${realSlug}.md`)
   const fileContents = fs.readFileSync(fullPath, 'utf8')
   const {
      data,
      content
   } = matter(fileContents)

   return {
      slug: realSlug,
      meta: data,
      content
   }
}
import matter from 'gray-matter'

const docsDirectory = join(process.cwd(), 'docs')

export function getDocBySlug(slug) {
   const realSlug = slug.replace(/\.md$/, '')
   const fullPath = join(docsDirectory, `${realSlug}.md`)
   const fileContents = fs.readFileSync(fullPath, 'utf8')
   const {
      data,
      content
   } = matter(fileContents)

   return {
      slug: realSlug,
      meta: data,
      content
   }
}

If you're using MDX, you can use a JS object directly.

// pages/posts/markdown-with-next.mdx

export const meta = {
   title: 'Markdown/MDX with Next.js',
   author: 'Lee Robinson'
}
// pages/posts/markdown-with-next.mdx

export const meta = {
   title: 'Markdown/MDX with Next.js',
   author: 'Lee Robinson'
}
load more v
65%

I have tried this pure component:

const RawHTML = ({children, className = ""}) => 
<div className={className}
  dangerouslySetInnerHTML={{ __html: children.replace(/\n/g, '<br />')}} />
Features

Takes classNameprop (easier to style it)
Replaces \n to <br /> (you often want to do that)
Place content as children when using the component like:
<RawHTML>{myHTML}</RawHTML>
I have placed the component in a Gist at Github: RawHTML: ReactJS pure component to render HTML

Share
75%

I've only used Next js router for basic page routing. How do I achieve client side component based (lazy-load?) routing? I can't imagine writing an app without it. How would you?,This is something we've been actively exploring solutions for, which will likely be related to React Server Components.,Each page could also export (or re-export) its own Layout function or component (similar to getStaticProps & getServerSideProps) to prevent messing with the Next.js directory structure and make it supported behaviour (enabling HMR). That could look something like this:,What about having a layout page(which doesnt render more than once) that connects to a socket server?

mysite.com /
   mysite.com / me
mysite.com / questions
load more v
40%

Absolutely Everything You Could Need To Know About How JavaScript Works. Seriously… this list is utterly exhaustive it covers more core concepts than I can hold the names of in working memory…bryanguner.medium.com,Practical Ways to Write Better JavaScript Solid methods of improving your JS. Tagged with javascript, webdev, beginners, react.dev.to,JavaScript essentials: why you should know how the engine works This article is also available in Spanish.medium.freecodecamp.org,Do the same for the Red only link. Everything should be working now.

console.log("bootcamp" [0]); // =&gt; "b"
console.log("bootcamp" [10]); // =&gt; "undefined"
console.log("boots" [1 * 2]); // =&gt; "o"
console.log("boots" ["boot".length - 1]); // =&gt; "t"
load more v
22%

This example reverses the children so that they appear in reverse order that we wrote it in. ,Limit the number of nodes fetched during development, so you can rapidly make changes to your site while creating new pages and features,This is just the beginning! The goal of this effort was to create a foundation on which we can, together, benchmark relative build times across popular static site generators.,Search is not one of those things that come out of the box with Jamstack. Some extra decisions and implementation are required.

Once the project is iniated, make sure to clone the new repository on GitHub to local, and install the dependencies:

git clone git @github.com: username / your - repository - name.git
cd your - repository - name
npm i

Look into the /studio/schemas/documents folder. There are schema files for our main content types: author, category, site settings, and posts. Each of the files exports a JavaScript object that defines the fields and properties of these content types. Inside of post.js is the field definition for categories:

{
   name: 'categories',
   type: 'array',
   title: 'Categories',
   of: [{
      type: 'reference',
      to: [{
         type: 'category'
      }]
   }]
},

Head over to /studio/schemas/documents/category.js. There is a simple content model for a category that consists of a title and a description. Now that we’re creating dedicated pages for categories, it would be handy to have a slug field as well. We can define that in the schema like this:

// studio/schemas/documents/category.js
export default {
   name: 'category',
   type: 'document',
   title: 'Category',
   fields: [{
         name: 'title',
         type: 'string',
         title: 'Title'
      },
      {
         name: 'slug',
         type: 'slug',
         title: 'Slug',
         options: {
            // add a button to generate slug from the title field
            source: 'title'
         }
      },
      {
         name: 'description',
         type: 'text',
         title: 'Description'
      }
   ]
}

Between the createBlogPostPages function and the line that starts with exports.createPages, we can add the following code. I’ve put in comments here to explain what’s going on:

// web/gatsby-node.js

// ...

async function createCategoryPages(graphql, actions) {
   // Get Gatsby‘s method for creating new pages
   const {
      createPage
   } = actions
   // Query Gatsby‘s GraphAPI for all the categories that come from Sanity
   // You can query this API on http://localhost:8000/___graphql
   const result = await graphql(`{
    allSanityCategory {
      nodes {
        slug {
          current
        }
        id
      }
    }
  }
  `)
   // If there are any errors in the query, cancel the build and tell us
   if (result.errors) throw result.errors

   // Let‘s gracefully handle if allSanityCatgogy is null
   const categoryNodes = (result.data.allSanityCategory || {}).nodes || []

   categoryNodes
      // Loop through the category nodes, but don't return anything
      .forEach((node) => {
         // Desctructure the id and slug fields for each category
         const {
            id,
            slug = {}
         } = node
         // If there isn't a slug, we want to do nothing
         if (!slug) return

         // Make the URL with the current slug
         const path = `/categories/${slug.current}`

         // Create the page using the URL path and the template file, and pass down the id
         // that we can use to query for the right category in the template file
         createPage({
            path,
            component: require.resolve('./src/templates/category.js'),
            context: {
               id
            }
         })
      })
}

Last, this function is needed at the bottom of the file:

// /web/gatsby-node.js

// ...

exports.createPages = async ({
   graphql,
   actions
}) => {
   await createBlogPostPages(graphql, actions)
   await createCategoryPages(graphql, actions) // <= add the function here
}

Now that we have the machinery to create the category page node in place, we need to add a template for how it actually should look in the browser. We’ll base it on the existing blog post template to get some consistent styling, but keep it fairly simple in the process.

// /web/src/templates/category.js
import React from 'react'
import {graphql} from 'gatsby'
import Container from '../components/container'
import GraphQLErrorList from '../components/graphql-error-list'
import SEO from '../components/seo'
import Layout from '../containers/layout'

export const query = graphql`
  query CategoryTemplateQuery($id: String!) {
    category: sanityCategory(id: {eq: $id}) {
      title
      description
    }
  }
`
const CategoryPostTemplate = props => {
  const {data = {}, errors} = props
  const {title, description} = data.category || {}

  return (
    <Layout>
      <Container>
        {errors && <GraphQLErrorList errors={errors} />}
        {!data.category && <p>No category data</p>}
        <SEO title={title} description={description} />
        <article>
          <h1>Category: {title}</h1>
          <p>{description}</p>
        </article>
      </Container>
    </Layout>
  )
}

export default CategoryPostTemplate

Even though we’re only defining the references in one type, Sanity’s datastore will index them “bi-directionally.” That means creating a reference to the “Structured content” category document from a post lets Sanity know that the category has these incoming references and will keep you from deleting it as long as the reference exists (references can be set as “weak” to override this behavior). If we use GROQ, we can query categories and join posts that have them like this (see the query and result in action on groq.dev):

*[_type == "category"] {
   _id,
   _type,
   title,
   "posts": * [_type == "post" && references( ^ ._id)] {
      title,
      slug
   }
}
// alternative: *[_type == "post" && ^._id in categories[]._ref]{

This ouputs a data structure that lets us make a simple category post template:

[{
      "_id": "39d2ca7f-4862-4ab2-b902-0bf10f1d4c34",
      "_type": "category",
      "title": "Structured content",
      "posts": [{
            "title": "Exploration powered by structured content",
            "slug": {
               "_type": "slug",
               "current": "exploration-powered-by-structured-content"
            }
         },
         {
            "title": "My brand new blog powered by Sanity.io",
            "slug": {
               "_type": "slug",
               "current": "my-brand-new-blog-powered-by-sanity-io"
            }
         }
      ]
   },
   // ... more entries
]

Add the following code to the /web/gatsby-node.js file, either below or above the code that’s already in there:

// /web/gatsby-node.js
// Notice the capitalized type names
exports.createResolvers = ({
   createResolvers
}) => {
   const resolvers = {
      SanityCategory: {
         posts: {
            type: ['SanityPost'],
            resolve(source, args, context, info) {
               return context.nodeModel.runQuery({
                  type: 'SanityPost',
                  query: {
                     filter: {
                        categories: {
                           elemMatch: {
                              _id: {
                                 eq: source._id
                              }
                           }
                        }
                     }
                  }
               })
            }
         }
      }
   }
   createResolvers(resolvers)
}

Now that we have the data we need, we can return to our category page template (/web/src/templates/category.js) and add a list with links to the posts belonging to the category.

// /web/src/templates/category.js
import React from 'react'
import {graphql, Link} from 'gatsby'
import Container from '../components/container'
import GraphQLErrorList from '../components/graphql-error-list'
import SEO from '../components/seo'
import Layout from '../containers/layout'
// Import a function to build the blog URL
import {getBlogUrl} from '../lib/helpers'

// Add “posts” to the GraphQL query
export const query = graphql`
  query CategoryTemplateQuery($id: String!) {
    category: sanityCategory(id: {eq: $id}) {
      title
      description
      posts {
        _id
        title
        publishedAt
        slug {
          current
        }
      }
    }
  }
`
const CategoryPostTemplate = props => {
  const {data = {}, errors} = props
  // Destructure the new posts property from props
  const {title, description, posts} = data.category || {}

  return (
    <Layout>
      <Container>
        {errors && <GraphQLErrorList errors={errors} />}
        {!data.category && <p>No category data</p>}
        <SEO title={title} description={description} />
        <article>
          <h1>Category: {title}</h1>
          <p>{description}</p>
          {/*
            If there are any posts, add the heading,
            with the list of links to the posts
          */}
          {posts && (
            <React.Fragment>
              <h2>Posts</h2>
              <ul>
                { posts.map(post => (
                  <li key={post._id}>
                    <Link to={getBlogUrl(post.publishedAt, post.slug)}>{post.title}</Link>
                  </li>))
                }
              </ul>
            </React.Fragment>)
          }
        </article>
      </Container>
    </Layout>
  )
}

export default CategoryPostTemplate
load more v

Other "markdown-rendered" queries related to "Next.JS - Gist's embedded in markdown rendered via dangerouslySetInnerHTML only show after full page load, not React route load"