nuxt / content

The file-based CMS for your Nuxt application, powered by Markdown and Vue components.
https://content.nuxt.com
MIT License
3.12k stars 623 forks source link

Rendering a Markdown string #924

Closed remino closed 2 years ago

remino commented 3 years ago

Is there a way, like a Nuxt plugin or Vue component, @nuxt/content provides to let you reuse its instance of remark to render a single Markdown string?

LukaHarambasic commented 3 years ago

I also would be interested in a solution as I right now use marked: https://github.com/LukaHarambasic/harambasic.de/blob/main/nuxt.config.js#L199

ChristianMurphy commented 3 years ago

@LukaHarambasic offers a good work around, I would add that for consistency consider using https://github.com/micromark/micromark as the markdown parser/transformer. Nuxt content uses remark/micromark https://github.com/nuxt/content/blob/0b6a7be6f6287cde880b0d37c88e6461efa167ff/packages/content/package.json#L47 (and https://github.com/remarkjs/remark/releases/tag/13.0.0) to handle markdown. Using the same parser/transformer for all content can guarantee the content will render the same way no matter where/how it is loaded on the page.

LukaHarambasic commented 3 years ago

Using the same parser/transformer for all content can guarantee the content will render the same way no matter where/how it is loaded on the page.

How can it be guaranteed? I'm not sure what magic does nuxt/content, okay we could look it up, but then we still need a way to get the same instance or an instance with the same options. Not relevant for my use case at all, just thinking out loud :)

ChristianMurphy commented 3 years ago

npm/yarn modules are singletons. if you don't install micromark at the top level, importing micromark will get the same instance of micromark that nuxt/content is using. You're right that nuxt applies some plugins on top, which may change the content https://github.com/nuxt/content/blob/939caf36c547a6b4af6e303c52ed5989a5dea2f0/packages/content/lib/utils.js#L14-L26 You could go a level higher in abstraction and use remark and rehype with these same plugins.

However, even without the plugins micromark and marked have some differences in how they parse markdown. Both aim to implement the CommonMark standard, but marked still has several unresolved CommonMark bugs (https://github.com/markedjs/marked/issues?q=is%3Aissue+is%3Aopen+commonmark+label%3A%22parser%3A+CommonMark%22) which can cause content to be parsed different.

CaptainFalcon92 commented 3 years ago

Hello.

I would like to add my own interest to this question.

I too would like to display markdown just like nuxt/content does, but loaded from an api call. Obviously all i get from the api is a string - and not an object compatible with :

<NuxtContent :document="{ body: product.description }" />

This won't work because product.description is a markdown string. That means i would first need to use the same markdown parser that nuxt/content uses.

Is there a way for us to use the power of the NuxtContent component while having markdown not fetched from the content directory ? (i.e. from api calls).

I can't use a different rendered because i wish to include components inside my markdown just like nuxt/content supports.

thanks !

cingh-jasdeep commented 2 years ago

Sat Sri Akal ji

Isn't this solved here? https://content.nuxtjs.org/advanced#contentfilebeforeinsert

we can scroll down to 2nd example with json to have a look

but still string content would have to be inside files in 'content' folder

rmrbytes commented 2 years ago

I too would be interested since the solution I am adopting is using @nuxtjs/markdownit' module,

<div v-html="$md.render(blog.description)"></div>

However, this fails when the markdown string has a reference to an image that is stored within the blog content page directory

// markdown
...
<img src="images/img.png">

![blog image](images/img.png)
Tahul commented 2 years ago

Hello :)

This should be solved by <Markdown /> component provided by @nuxt/content v2.

I am closing this issue as there is no plan to provide this feature for v1 versions.

Feel free to reopen with a PR if you manage to add this in v1.

You can find documentation about this feature for v2 here: https://content-v2.nuxtjs.org/guide/syntax/mdc#markdown-rendering

abreumatheus commented 2 years ago

Hello :)

This should be solved by <Markdown /> component provided by @nuxt/content v2.

I am closing this issue as there is no plan to provide this feature for v1 versions.

Feel free to reopen with a PR if you manage to add this in v1.

You can find documentation about this feature for v2 here: https://content-v2.nuxtjs.org/guide/syntax/mdc#markdown-rendering

@Tahul

I don't understand how this would be solved using the <Markdown /> component, as it seems to act as a substitute to a slot. Could you provide an example of how to render a string using the compoment? Also, the link you posted is broken.

timgoeller commented 2 years ago

I'd like to push this aswell, since the answer does indeed not solve the problem stated in the issue. There was a way to import and use the Markdown Parser in the old version of nuxt content, but the new version does not export it anymore.

Lyokolux commented 2 years ago

Hello :)

This should be solved by <Markdown /> component provided by @nuxt/content v2.

I am closing this issue as there is no plan to provide this feature for v1 versions.

Feel free to reopen with a PR if you manage to add this in v1.

You can find documentation about this feature for v2 here: https://content-v2.nuxtjs.org/guide/syntax/mdc#markdown-rendering

How can I solve it? I have a string as input, but I don't see how can I use it.

Yiddishe-Kop commented 2 years ago

I also needed this, and figured it out by source diving 😇

Here is a minimal working example:

import markdownParser from "@nuxt/content/transformers/markdown";

const markdownString = '<your-markdown-string>'

const parsedMarkdown = await markdownParser.parse(
    `<some-id>`,
    markdownString
);

Your parsedMarkdown AST can then be rendered like so:

<ContentRenderer :value="parsedMarkdown" />
unguul commented 1 year ago

I don't understand why this is closed. It's still not clear what is the official way of just rendering some markdown that does not come from a file.

green-mike commented 1 year ago

Need to parse string content by same parser and plugin in some condition. It would be useful if repo export parser or component.

Mnigos commented 1 year ago
import markdownParser from "@nuxt/content/transformers/markdown"

Cannot import from "@nuxt/content/transformers/markdown" - module not found

fr-eed commented 1 year ago
import markdownParser from "@nuxt/content/transformers/markdown"

Cannot import from "@nuxt/content/transformers/markdown" - module not found

<template>
  <ContentRenderer :value="parsedMarkdown" />
</template>

<script>
import { defineComponent } from 'vue'

import markdownParser from '@nuxt/content/transformers/markdown'

export default defineComponent({
  components: {},

  // define props
  props: {
    md: {
      type: String,
      default: '',
    },
    cid: {
      type: String,
      default: '<some-id>',
    },
  },

  // init
  async setup(props) {
    return {
      parsedMarkdown: await markdownParser.parse(props.cid, props.md),
    }
  },
})
</script>

^^ This worked for me

Lyokolux commented 1 year ago
import markdownParser from "@nuxt/content/transformers/markdown"

Cannot import from "@nuxt/content/transformers/markdown" - module not found

Which version are you using? and @fr-eed too

fr-eed commented 1 year ago
import markdownParser from "@nuxt/content/transformers/markdown"

Cannot import from "@nuxt/content/transformers/markdown" - module not found

Which version are you using? and @fr-eed too

"@nuxt/content": "2.3.0"

garbit commented 1 year ago

I'm also having the same issue on v2.3.0

piscis commented 1 year ago

I'm using @nuxt/content v2.4.3 and it still works. I noticed that you have to register @nuxt/content in your nuxt.config.ts modules section otherwise the parser does not work.

Steps I did to make it work:

Step one: Install @nuxt/content

pnpm i -D @nuxt/content

Step two: Add nuxt content to your modules section

export default defineNuxtConfig({
  modules: [
  ...
    '@nuxt/content',
  ],
...

Step three: Implement a component that uses the markdown parser

<script setup lang="ts">
// @ts-expect-error avoid lint error
import markdownParser from '@nuxt/content/transformers/markdown'

export interface MarkdownRenderProps {
  md?: string
  cid?: string
}
const props = withDefaults(defineProps<MarkdownRenderProps>(), { md: '', cid: '<some-id>' })
const parsedMarkdown = await markdownParser.parse(props.cid, props.md)
</script>

<template>
  <ContentRenderer :value="parsedMarkdown" v-bind="$attrs" />
</template>
Elektronenhirn108 commented 1 year ago

Same problem for me: @nuxt/content/transformers/markdown does not exist.

sevspo commented 1 year ago

Same here: Same problem for me: @nuxt/content/transformers/markdown does not exist.

ManUtopiK commented 1 year ago

I'm wondering how to convert the parsedMardown to html ? I need to pass this html to a tiptap editor. Or how you can programmatically use ContentRender to get the html output ?

leosin commented 1 year ago

highlight not working.

leminhluan4244 commented 1 year ago

Same here: Same problem for me: @nuxt/content/transformers/markdown does not exist. This version: "@nuxt/content": "^2.6.0"

import markdownParser from "@nuxt/content/transformers/markdown";

const markdownString = '<your-markdown-string>'

const parsedMarkdown = await markdownParser.parse(
    `<some-id>`,
    markdownString
);

Error message: Cannot find module '@nuxt/content/transformers/markdown' or its corresponding type declarations.ts(2307)

piscis commented 1 year ago

Maybe this helps to get thinks started:

CodeSandbox Demo

Git Repo

ManUtopiK commented 1 year ago

Maybe this helps to get thinks started

Thanks @piscis for your demo!

Personally, I tried this and it works perfectly! But this is not I am looking for exactly.

I don't want to render the output in the page. I want the HTML output of the rendered markdown to pass this HTML to some libraries like tiptap editor. markdownParser.parse() under the hood use remark to convert markdown to mdast (Markdown Abstract Syntax Tree), and a custom nuxt-content rehype plugin that convert this mdast to an hast (HTML Abstract Synt...). This hast is used by the ContentRendererMarkdown component to output the html using Vue VNode.

Actually, there's no way to get the HTML output on the process. We should build our own parser with rehypeStringify for example. In our CodeSandbox Demo line app.vue:12, the variable parsedMarkdown doesn't contain the HTML, but the hast.

Rosinida commented 1 year ago

Thanks @piscis for your demo.

Would it be possible to use this setup for in place rendering as well? I would like to use something a textarea field for rendering Md in realtime.

mimoo commented 1 year ago

do you guys know how to use nice syntax highlighting when doing this?

maybe the answer is here: https://github.com/nuxt/content/issues/1490

thaycacac commented 1 year ago

It worked for me https://codesandbox.io/p/github/piscis/nuxt3-markdown-renderer-codingsandbox/main?file=/app.vue:12,1-12,74 with version 2.7.0

AleeeKoi commented 1 year ago

I'm using @nuxt/content v2.4.3 and it still works. I noticed that you have to register @nuxt/content in your nuxt.config.ts modules section otherwise the parser does not work.

Steps I did to make it work:

Step one: Install @nuxt/content

pnpm i -D @nuxt/content

Step two: Add nuxt content to your modules section

export default defineNuxtConfig({
  modules: [
  ...
    '@nuxt/content',
  ],
...

Step three: Implement a component that uses the markdown parser

<script setup lang="ts">
// @ts-expect-error avoid lint error
import markdownParser from '@nuxt/content/transformers/markdown'

export interface MarkdownRenderProps {
  md?: string
  cid?: string
}
const props = withDefaults(defineProps<MarkdownRenderProps>(), { md: '', cid: '<some-id>' })
const parsedMarkdown = await markdownParser.parse(props.cid, props.md)
</script>

<template>
  <ContentRenderer :value="parsedMarkdown" v-bind="$attrs" />
</template>

HI, is there a way to unwrap the p tag before rendering the parsed content?

I'm trying as following but it's doesn't work..

const { unwrap } = useUnwrap()

const parsedMarkdown = await markdownParser
  .parse(`<some-id>`, props.md)
  .then((vnode: any) => unwrap(vnode, ['p']))

where am i wrong? Thanks

tveimo commented 1 year ago

It's now (since nuxt/content v2.8.1 / @nuxtjs/mdc v0.1.3) possible to use @nuxtjs/mdc instead, see details here; https://github.com/nuxt-modules/mdc, essentially you'll do

import { parseMarkdown } from '@nuxtjs/mdc/runtime'

async function main(mdc: string) {
  const ast = await parseMarkdown(mdc)

  // Do your magic with parsed AST tree

  return ast
}
AleeeKoi commented 1 year ago

It's now (since nuxt/content v2.8.1 / @nuxtjs/mdc v0.1.3) possible to use @nuxtjs/mdc instead, see details here; https://github.com/nuxt-modules/mdc, essentially you'll do

import { parseMarkdown } from '@nuxtjs/mdc/runtime'

async function main(mdc: string) {
  const ast = await parseMarkdown(mdc)

  // Do your magic with parsed AST tree

  return ast
}

Hi @tveimo ! Cool, in my case I already switched to vue-showdown creating a custom component.

Are there any significant differences with @nuxtjs/mdc ?

adamalfredsson commented 1 year ago

With @nuxt/content/transformers/markdown, how does one use the remark/rehype plugins as defined in the nuxt.config file?

marquaye commented 10 months ago

It's now (since nuxt/content v2.8.1 / @nuxtjs/mdc v0.1.3) possible to use @nuxtjs/mdc instead, see details here; https://github.com/nuxt-modules/mdc, essentially you'll do

import { parseMarkdown } from '@nuxtjs/mdc/runtime'

async function main(mdc: string) {
  const ast = await parseMarkdown(mdc)

  // Do your magic with parsed AST tree

  return ast
}

With v0.2.8 of @nuxtjs/mdc it is even simpler. Just use their <MDC /> component

<script setup lang="ts">
const exampleMarkdownString = `##Hello MDC`
</script>

<template>
  <MDC :value="exampleMarkdownString" tag="article" />
</template>
bbhxwl commented 8 months ago

The following code can run normally, but there are warnings. How should I handle this?

截屏2024-02-21 23 59 56
jeannen commented 7 months ago

This works fine, but it seems like line breaks are not properly parsed. One line break = Ignored, 2 line break only works but for the first one image

FabianMontoya commented 4 months ago

The following code can run normally, but there are warnings. How should I handle this?

截屏2024-02-21 23 59 56

I'm having the same issue:

image