Open TixieSalander opened 4 years ago
As far as I know, this is currently not supported out of the box.
Maybe you can solve this with a component that dynamically loads the image for you, something like this? https://codesandbox.io/s/nuxt-content-106-hwhbv?file=/content/post.md
Yeah I thought about a solution like that, but not really compliant with content management tools :/
Hi there,
The main issue here is that @nuxt/content is independent of Webpack, meaning that if you change anything in content/
you can directly run nuxt export
(Nuxt 2.13) without having to wait for Webpack to build.
We are thinking of a way to support assets/images/
with Webpack dynamic import so it can still support nuxt export
without having to build again (expect if you add an image in assets/images/
)
I see it was removed from the todo list. Any update on the situation of this feature request?
Bumping, would love an update seeing as it has been removed from the To do?
Don't pay too much attention to the todo list, we use it internally to plan the next release.
We have considered this issue and will be working on it soon.
The tricky part is to not involve Webpack for it when using images since it would force a rebuild and loose te speed of content update. This is why it is not an such easy task 😁
@Atinux might overwriting the html img tag by a custom component with the same attributes work for this case? Maybe even as an opt in? 🤔
Indeed your solution was quite good actually, if you can open a PR so we can work on it it would be great :)
The tricky part is to not involve Webpack
@Atinux Maybe I'm missing something but why wouldn't I want webpack involved doing it's thing? Wouldn't I want webpack to rerun if content involving a webpack asset gets updated?
The idea behing content is that you can change your Markdown without rebuilding your entire Nuxt app, making it faster redeployment.
Tracking assets is a bit more tricky in that case since Content does not depend of Webpack at all.
I fiddled around with it today and think I understood the way in which it could be implemented.
I would be really interested in your thoughts about the API design. Should we support both, markdown images and HTML tags?
![image alt text](~/assets/localimage.jpg)
<img src="~/assets/localimage.jpg" alt="image alt text>
I have a few doubts about using the default tags and attributes because in this case, we would've to check if the developer is trying to load a local asset or remote image.
Maybe we could avoid this check by adding a custom attribute:
<img local-src="~/assets/localimage.jpg" alt="image alt text>
Yeah both should be supported, this will give more control since you can't do basic things like center an image with markdown
Is it possible to store the images under the content directory itself instead of in the assets directory? Then you could create a structure like:
content/articles/my-new-article/index.md content/articles/my-new-article/img/pic1.jpg content/articles/my-new-article/img/pic2.jpg content/articles/my-new-article/img/pic3.jpg
which would keep all the content together.
Yes but this should not be the only solution.. again to have more control pointing to a specific folder is useful in case someone wants to use a CDN/reverse proxy to serve the images.
@davydnorris suggestion was helpful to me
I created a global Image
component to work around with that and stored my images inside the /content/blog/my-blog-slug/images/
<template>
<img :src="imgSrc()" :alt="alt" />
</template>
<script>
export default {
props: {
src: {
type: String,
required: true
},
alt: {
type: String,
required: true
}
},
methods: {
imgSrc() {
try {
return require(`~/content${this.src}`)
} catch (error) {
return null
}
}
}
}
</script>
Make sure to register this component globally.
Inside components/global/Image.vue
and set the components:true
in nuxt.config.js
<image
v-if="blog.Featured_Image"
:src="blog.dir + '/images/' + blog.Featured_Image"
:alt="blog.Title"
>
</image>
Inside Markdown:
<image src="/blog/my-blog-slug/images/Scripts.png" alt="Scripts"></image>
I hope this will be helpful to someone. 🙌
I did the same thing but I am getting warnings when I compile the files of the form:
WARNING in ./content/blog/2020-02-15-blog-number-3/index.md 1:2
Module parse failed: Assigning to rvalue (1:2)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> ---
| title: 'Blog #3 Headline'
| description: Lorem Ipsum type stuff
@ . sync ^\.\/content.*$ ./content/blog/2020-02-15-blog-number-3/index.md
@ ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./components/BlogImage.vue?vue&type=script&lang=js&
@ ./components/BlogImage.vue?vue&type=script&lang=js&
@ ./components/BlogImage.vue
@ ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./pages/blog/_slug.vue?vue&type=script&lang=js&
@ ./pages/blog/_slug.vue?vue&type=script&lang=js&
@ ./pages/blog/_slug.vue
@ ./.nuxt/router.js
@ ./.nuxt/index.js
@ ./.nuxt/client.js
@ multi ./.nuxt/client.js
I thought I could ignore the warning, however when I then try to run export or dev, the process hangs and never completes, and I see the above warning in the generated javascript instead of the actual content.
I'm not a webpack/babel config file expert, but it looks like the error is from webpack when it goes looking for the image files. I assume you could write a rule to exclude the md files somehow? But then would that mess up the content generation?
Any help would be greatly appreciated
Unfortunately, this is the only solution worked for me. I have the same warnings. However, it doesn't affect the build process.
OK so I have made progress...
I couldn't work out why my images were being converted during the build but their paths weren't - then I realised that the images I happened to be using were IMG_xxxx.JPG and I had them in the code as IMG_xxxx.jpg. As soon as I made them exactly the same they all got picked up.
Still trying to get the custom image component inside the .md to work - @MuhaddiMu did you register yours as a global?
@davydnorris My Image component is registered globally. I have a working example, you can check that too by cloning https://github.com/MuhaddiMu/Portfolio
Many thanks - I have now got it working 🥇
The components directive doesn't seem to do exactly what it says in the guide. I had set up a subdirectory specifically for blog components, called 'content', and had registered that in my nuxt config with:
components: [
'~/components/content'
]
but it didn't seem to work. I then moved the components into the top level and set components:true
, but still no luck.
It finally worked when I copied your example exactly and added a 'global' subdirectory with components:true
.
Thank you for all your assistance!!
Besides images used in HTML and markdown, what about images referenced in frontmatter? I've had to add these to static/images
in order for this to work instead of assets
.
img: /images/journey-ux.jpg
alt: Rocket soaring through space
This were actually the first ones I got to work. I used the same approach as above but could use a normal component in my _slug.vue and preview components:
<template>
<div>
<v-img
:src="require(`@/content${post.dir}/img/${post.image}`)"
:alt="post.title"
height="400"
/>
<h1>{{ post.title }}</h1>
<p>
{{ post.description }}
</p>
<nuxt-content
:document="post"
/>
</div>
</template>
<script>
export default {
async asyncData ({ params, error, $content }) {
try {
const postPath = `/blog/${params.slug}`;
const [post] = await $content('blog', { deep: true })
.where({ dir: postPath })
.fetch();
return { post };
} catch (err) {
error({
statusCode: 404,
message: 'Page could not be found'
});
}
}
};
</script>
in your case you've already included the image subdirectory so just adjust the template accordingly
@davydnorris Regarding the Module parse failed: Assigning to rvalue (1:2)
error. Using ignore-loader solved this for me:
// nuxt.config.js
// ...
build: {
extend(config, { isDev, isClient }) {
config.module.rules.push({
test: /\.md$/i,
loader: 'ignore-loader'
})
}
}
// ...
Many thanks @lucien144 - that's exactly what I was hoping someone would know! Will try that tomorrow
My original point was to be able using the webpack asset management for images (compression, cache-busting), while writing posts on content management tools. For that, it's needs to work with vanilla , and not some custom vuejs elements.
Hi there,
The main issue here is that @nuxt/content is independent of Webpack, meaning that if you change anything in
content/
you can directly runnuxt export
(Nuxt 2.13) without having to wait for Webpack to build.We are thinking of a way to support
assets/images/
with Webpack dynamic import so it can still supportnuxt export
without having to build again (expect if you add an image inassets/images/
)
What about linking to assets that are not images? For example, linking to a local pdf file with [A file](/assets/sample.pdf)
in the markdown? It's not mentioned on this thread but I presume it's essentially the same issue?
Yes. Assets from Markdown written with fully qualified URL ~/images/ to be resolvable either from Vue pages/ or components/ components as equal to from a Markdown file. I'm assuming we could have the place where to store those as configurable. Either from static, or from content, or assets. So we avoid having more than one notation for the same resource and the resource resolver in its context (remark's toTHML, or WebPack, or vite) to pick which ever its running.
So many ugly crutches just to render image from Markdown, oh God. Nuxt is so 'intuitive' that I had less time to do it by myself with 11ty or at my self made ssg on Python.
extend(config, { isDev, isClient }) { config.module.rules.push({ test: /\.md$/i, loader: 'ignore-loader' }) }
now page with image doesn't render at all, thank you very much!
My original point was to be able using the webpack asset management for images (compression, cache-busting), while writing posts on content management tools. For that, it's needs to work with vanilla , and not some custom vuejs elements.
@TixieBorg - I've found that being able to use custom vuejs within the markdown has been amazingly useful. By wrapping my images in a Vuejs component I have got total control of the size, positioning, wrapping, etc. This is not available in markdown.
Webpack still operates on the images and does everything you need, but as part of Vuejs, not markdown.
I have to say, I absolutely love nuxt/content - it's exactly what I needed for my purpose.
@Defite - not sure what you have done, but that rule works perfectly for me.
Note that the rule is not required, it's just there to shut up any warnings - I still got a working site without it. If you remove it, does your page work?
@Defite - not sure what you have done, but that rule works perfectly for me.
Note that the rule is not required, it's just there to shut up any warnings - I still got a working site without it. If you remove it, does your page work?
I tried different variants with images placed in:
~/assets/images
~/content/images
~/content/post/post.jpg
In my markdown file I use it like this:
<v-img src="post.jpg" />
Every time I get post.jpg
in resulting code or page doesn't render =(
@Defite Well, some error log would be helpful... Looks like you are using some image component without specifying path (Vuetify perhaps?) so I would suspect the problem is in the component and not in the Nuxt's content module. Using simple img
tag or markdown's ![]()
works fine?
@Defite Well, some error log would be helpful... Looks like you are using some image component without specifying path (Vuetify perhaps?) so I would suspect the problem is in the component and not in the Nuxt's content module. Using simple
img
tag or markdown's![]()
works fine?
Simple img tag (inside global component VImg) works fine. But when I try to put there <nuxt-image>
I got ERROR Cannot read property 'validate' of undefined
and this in image source: /_image/ipx/local/_/_/_nuxt/assets/images/my.png
. So may be I must use nuxt-image
in Markdown?
I'll provide repo to show current state of my test.
Here is repo https://github.com/Defite/nuxt-image-test.
https://github.com/Defite/nuxt-image-test/blob/master/components/global/VImg.vue I included both working and not working variants. Simple img
is working ok.
https://github.com/Defite/nuxt-image-test/blob/master/content/blog/image-post.md - markdown file
So many ugly crutches just to render image from Markdown, oh God. Nuxt is so 'intuitive' that I had less time to do it by myself with 11ty or at my self made ssg on Python.
And sorry for being toxic, I'm just too tired of recreating simple solution third time for another framework. My understanding of world is just too idealistic, I just wanna to be sure that all things inside one ecosystem work perfectly. And it was really disappointing to realize that it's not.
@Defite - I am using Vuetify, with Vuejs, with nuxt, with nuxt/content...
If I can get it working, with some problems, but nothing that took more than a few hours of effort, and with the end result that's perhaps even better than out-of-the-box...
You can do it my friend :-)
Or do it in Python as you said - I know both, and for what I need I am really happy here
So many ugly crutches just to render image from Markdown, oh God. Nuxt is so 'intuitive' that I had less time to do it by myself with 11ty or at my self made ssg on Python.
And sorry for being toxic, I'm just too tired of recreating simple solution third time for another framework. My understanding of world is just too idealistic, I just wanna to be sure that all things inside one ecosystem work perfectly. And it was really disappointing to realize that it's not.
PS: I am 54... if you're older than me - get with the young kids and get off the punch cards. If you're younger than me, then omg shut the eff up and actually code something- us oldies got 20 more languages than you!!! ;-)
Well, every day I code literally tons of code. I just don't understand why the exact product is not working. Sure, it's opensource and bla bla, but why? People who made it didn't think about how it really must work?
Ok, it's offtop. Thanks, I got ya.
@Defite <nuxt-image>
is not ready yet, we are actively working on the stable version, see https://github.com/nuxt/image/pull/99
Please stay polite, we all do our best to make it as good as possible.
@Defite
<nuxt-image>
is not ready yet, we are actively working on the stable version, see nuxt/image#99Please stay polite, we all do our best to make it as good as possible.
Great news. And again, sorry for being toxic here. Rough day.
I am using Vuetify like you. This is what I have in my site - pretty much exactly what is further up the thread
BlogImage.vue
<template>
<v-img
:src="imgSrc()"
:alt="alt"
/>
</template>
<script>
export default {
props: {
dir: {
type: String,
required: true
},
src: {
type: String,
required: true
},
alt: {
type: String,
required: true
}
},
methods: {
imgSrc () {
try {
return require(`~/content${this.dir}/img/${this.src}`);
} catch (e) {
console.log(e);
}
}
}
};
</script>
This is sitting in the components/global folder.
Then I have arranged my blog so that all images are stored under an img folder, and within the markdown I have
## Displaying content
<blog-image dir="/blog/2019-12-20-blog-number-2" src="IMG_6264.JPG" alt="text" />
Display stuff
And everything works for me. I have since added props to size and position the image as well
I am using Vuetify like you. This is what I have in my site - pretty much exactly what is further up the thread
BlogImage.vue
<template> <v-img :src="imgSrc()" :alt="alt" /> </template> <script> export default { props: { dir: { type: String, required: true }, src: { type: String, required: true }, alt: { type: String, required: true } }, methods: { imgSrc () { try { return require(`~/content${this.dir}/img/${this.src}`); } catch (e) { console.log(e); } } } }; </script>
This is sitting in the components/global folder.
Then I have arranged my blog so that all images are stored under an img folder, and within the markdown I have
## Displaying content <blog-image dir="/blog/2019-12-20-blog-number-2" src="IMG_6264.JPG" alt="text" /> Display stuff
And everything works for me. I have since added props to size and position the image as well
I'm not using Vuetify)
OK well, whatever you're using substitute the appropriate image component, or just use img
Here are what I tried and found.
First, I created a rehype plugin, which is inspired by a comment by @dtmzr.
@Atinux might overwriting the html img tag by a custom component with the same attributes work for this case? Maybe even as an opt in? 🤔
This rehype plugin detects img
nodes in AST and replaces them with app-image
node, when src
attribute starts with "."
, "~"
, or "@"
(cf. how vue-loader converts paths to require
function call).
Second, I defined a global component AppImage
in Nuxt app (i.e. ~/components/global/AppImage.vue
). AppImage
accepts 2 props, src
and alt
and renders a single img
element. While value of alt
prop is directly bound to alt
attribute of the img
element, value of src
prop is modified before being binding to require
function call.
AppImage
removes the part ~/assets/
from src
value and concatenates them again when calling require
function in the template block. In this way, webpack can detect require with expression and can be aware of files in ~/assets/
directories in the bundle.
Actually, this point is where everyone faced at many warning messages like @davydnorris reported:
I did the same thing but I am getting warnings when I compile the files of the form:
WARNING in ./content/blog/2020-02-15-blog-number-3/index.md 1:2 Module parse failed: Assigning to rvalue (1:2) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders > --- | title: 'Blog #3 Headline' | description: Lorem Ipsum type stuff @ . sync ^\.\/content.*$ ./content/blog/2020-02-15-blog-number-3/index.md [omit]
webpack has to include all the files whose path possibly match the expression in require
function call to handle all the cases. This is why files which don't have the associated loaders are warned.
You can see it in developer tool that a map of file paths which webpack is aware of. Screenshot below describes the case where only files which can match "~/assets/**/*"
are managed by webpack.
My implementation is just a proof of concept, so there would be many points to improve, for example:
content
directory@
srcset
(I didn't test it)assets
directory by itself for css filesThere are also some limitations. One is that changes on images are not watched and do not trigger app rebuilding. To see the newest images, let webpack require
the images again (page refreshing, I guess).
@Atinux
I am thinking of creating a draft PR for further discussion, which introduces another package to Nuxt Content repository (i.e. packages/rehype-assets
).
Perhaps, have you already found some solution for this issue?
I'm trying to get images to work, for me, images work on initial page load if coming at a URL from Google, but as soon as you refresh the page, the images loaded using a Nuxt JS component via my *.md
files disappear, how can I fix this?
Each article, md
file has:
<dynamic-image image-alt="how does website downtime affect a business" file="blogs/articles/actual-costs-of-website-downtime/business-affects.jpg"></dynamic-image>
Which is importing the component, and the component looks like...
<template>
<b-img :src="dynamicImage" :alt="imageAlt" fluid class="mt-1 mb-3 rounded"></b-img>
</template>
<script>
export default {
props: {
file: {
type: String,
required: true
},
imageAlt: {
type: String,
default: 'FREE Domain Monitor'
}
},
computed: {
dynamicImage() {
return require(`~/assets/images/content/${this.file}`)
}
}
}
</script>
In the docs, it should be mentioned that images in markdown are not supported yet. It's no fun to go through all the setup just to realize at the end of it that they don't work. This is such a basic feature.
EDIT: found a good workaround here https://gilberttanner.com/blog/creating-a-blog-with-nuxt-content#gettingimagestowork
I did the same thing but I am getting warnings when I compile the files of the form:
WARNING in ./content/blog/2020-02-15-blog-number-3/index.md 1:2 Module parse failed: Assigning to rvalue (1:2) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders > --- | title: 'Blog #3 Headline' | description: Lorem Ipsum type stuff @ . sync ^\.\/content.*$ ./content/blog/2020-02-15-blog-number-3/index.md @ ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./components/BlogImage.vue?vue&type=script&lang=js& @ ./components/BlogImage.vue?vue&type=script&lang=js& @ ./components/BlogImage.vue @ ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./pages/blog/_slug.vue?vue&type=script&lang=js& @ ./pages/blog/_slug.vue?vue&type=script&lang=js& @ ./pages/blog/_slug.vue @ ./.nuxt/router.js @ ./.nuxt/index.js @ ./.nuxt/client.js @ multi ./.nuxt/client.js
I thought I could ignore the warning, however when I then try to run export or dev, the process hangs and never completes, and I see the above warning in the generated javascript instead of the actual content.
I'm not a webpack/babel config file expert, but it looks like the error is from webpack when it goes looking for the image files. I assume you could write a rule to exclude the md files somehow? But then would that mess up the content generation?
Any help would be greatly appreciated
To make the WARNING go away you MUST add the image ext '.jpg' or '.gif' or ..., to the require call, this will filter out all the other files like '.md' and only targeted image file will be parsed. It's also very IMPORTANT to start the require with '~/assets/'
or '~/content/'
including all path separators, for example require(`~/assets/${this.file}.jpg`)
, please read here require-with-expression
Here is my solution for requiring images from assets
and content
in markdown files without using html component notation in markdown, only standard markdown image notation.
Dir structure: assets/logo1.svg content/post1/index.md content/post1/logo1.svg content/post1/img/logo2.svg content/post2/img/logo.svg
In 'index.md' file:
Content relative
![Logo](./logo1.svg "Logo Text")
![Logo](./image/logo2.svg "Logo Text")
Content absolute
![Logo](~/content/post2/img/logo.svg "Logo Text")
Assets absolute
![Logo](~/assets/logo1.svg "Logo Text")
Solution:
img
tags in content-img
content-img
(must be located in components/global/ContentImg.vue to be loaded for '.md') that knows how to handle the require
paths(described above) from the '.md' filesAll code is located in above code sandbox, see plugins/rehype-content-image
and components/global/ContentImg
@davydnorris
imgSrc () { try { return require(`~/content${this.dir}/img/${this.src}`); } catch (e) { console.log(e); }
## Displaying content <blog-image dir="/blog/2019-12-20-blog-number-2" src="IMG_6264.JPG" alt="text" /> Display stuff
I see you're still passing in the directory path manually. Is there any way to avoid that and have imgSrc()
figure out the directory of the .md
file itself? I'd really love to be able to just say <v-img src="image.jpg"/>
.
I would like to know if it's possible to have the webpack assets resolution inside of markdown content ? To display webpack handeled's images.
Reproduction link: https://codesandbox.io/s/dawn-snowflake-fv7l4?file=/content/post.md
1) I have an image in the assets folder 2) I link the image in the md file
![](~/assets/photo.jpg)
(or<img src="~/assets/photo.jpg">
in html) the same way I would have in a component templateI'm not interested in putting files in the static folder since there is no compression and no hash for cache-busting.