Assets
Assets are any non-code (js/css/html) files (images, fonts, json, etc) that can be imported from different places.
There are 3 types of assets that we have to deal with:
- Assets imported from TypeScript or CSS files, that will be processed by webpack.
- Assets referenced from HTML or loaded from TypeScript (e.g. using
fetch
), which are copied over manually. - Assets linked to in the page data files or storybook mock files, which should normally come from the CMS, and are only copied over when performing a preview build. These assets can be imported through webpack (preferred) or referenced as a string (not recommended).
Make sure to understand when and how to use each of them by reading the more detailed information below.
Webpack Assets
These assets are imported in TypeScript or referenced in SCSS files in a way that webpack understands them. This causes them to be automatically copied to the dist
folder on a build, and webpack adds a [contenthash]
in the filename to bust the cache if the file contents changed.
Source locations
These files can be placed in two locations:
- In
src/assets
for "global" assets used by multiple components. Ideally subfolders are created for each asset type. - In your component, e.g.
src/components/organisms/o35-something/images/pattern-1.png
if only a single component uses them.
Output locations
These files will be outputted based on type in the place: dist/site/static/[type]/[name].[chunkhash][ext]
- Example image:
dist/site/static/img/component-test.68df72aa.jpg
- Example font:
dist/site/static/fonts/font-name.68df72aa.woff2
You shouldn't have to know where they are outputted, since, those paths are included inside your bundled JavaScript and CSS files by webpack, so will be loaded automatically.
Asset imports / references
These assets should be referenced using relative paths, use like any other file processed by webpack.
- from TypeScript:
import componentImage from './images/component-test.jpg';
- from CSS:
background-image: url('../../../assets/images/webpack-test.jpg');
Examples
Loading an image from within your component folder.
// src/components/atoms/image-test/ImageTest.ts
import { defineComponent, computed, bind } from '@muban/muban';
// this will be handled by webpack, `componentImage` will contain the URL to the image
import componentImage from './images/component-test.jpg';
import './image-test.scss';
export const ImageTest = defineComponent({
name: 'image-test',
refs: {
jsImage: 'js-image',
},
setup({ refs }) {
return [bind(refs.jsImage, { attr: { src: computed(() => componentImage) } })];
},
});
Loading a "global" image from your CSS file.
// src/components/atoms/image-test/ImageTest.styles.scss
[data-component='image-test'] {
.css-image {
// loads a global asset, webpack will replace this with the output file URL
// TODO: potentially we can create an import shortcut to this global folder
background-image: url('../../../assets/images/webpack-test.jpg');
height: 300px;
background-position: center;
background-size: cover;
}
> div > img {
height: 300px;
}
}
TODO: Add example for using seng-css paths
$pathAsset
– default project asset path$pathFont
– default project font path (font, prefixed with$pathAsset
)$pathImage
– default project image path (image, prefixed with$pathAsset
)
Site Assets
These assets are imported without webpack knowing about them, and are copied over using the CopyWebpackPlugin
. Anything in your HTML files that should not be CMSable (e.g. part of the design or function).
Source locations
These files exist in a single location, with 2 dedicated folders based on the use.
- In
public/
for anything that needs to end up in the webroot (only for very few specific files). - In
public/static/
for anything else, like images, videos or json files. Ideally with a sub-folder for each type.
Output locations
These files will be copied over as is to the dist folder.
public/
>dist/site/
public/static/
>dist/site/static/
Asset imports / references
These assets should be referenced absolutely, beginning with /static/
.
- from HTML:
<img src="/static/images/template-test.jpg" />
- from TypeScript:
fetch('/static/json/data.json');
publicPath
If you specify a different publicPath
through the --public-path
argument when running npm run build
, you should prepend process.
to all of your assets paths.
Examples
Loading an image inside your template.
// src/components/atoms/image-test/ImageTest.template.ts
export function imageTestTemplate({}) {
return html`<div data-component="image-test">
<div>
<h1>Template Image</h1>
<!-- TODO: publicPath -->
<img src="/static/images/template-test.jpg" />
</div>
</div>`;
}
Loading JSON inside your TypeScript files.
// src/components/atoms/image-test/ImageTest.template.ts
export async function loadData() {
// TODO: publicPath
return fetch('/static/json/data.json');
}
Data Assets
These assets should only be used in your page data files or story mock files. If you import them through webpack, these files are only processed as part of storybook or a preview build. The files in the pages/public/static
folder are also copied over manually using the CopyWebpackPlugin
for npm run build -- --preview
builds, in case you reference these files as strings (which has some caveats).
On your production website, generally these assets will be managed by the CMS, so this is purely for mocking / previewing.
Source locations
These files can exist in two locations:
src/pages/public/static/
only to be used by yoursrc/pages/**/*.ts
data files.- Inside your component files, only used by mock data / story files, like
src/components/atoms/image-test/mocks/mock-component-test.jpg
.
Output locations
These files will be copied over as is to the dist folder.
- Webpack
src/pages/static/imags/foo.jpg
>dist/site/static/images/component-test.68df72aa.jpg
src/components/atoms/image-test/mocks/mock-component-test.jpg
>dist/site/static/images/mock-component-test.38df32ab.jpg
- Copy:
src/pages/public/static/
>dist/site/static
WARNING
The src/pages/public/static/
folder will be merged with the public/static/
folder, so make sure the filenames don't clash, or use "each others" files.
Asset imports / references
To have this work in all cases, it's best to always use import
/require
to reference these mock assets. Another option is to use string values starting with /static
/.
- from TS page data using require (recommended):
// top of file
import mockTest from './static/images/mock-test.jpg';
// in template data function
props: {
dataImage: mockTest;
}
- from TS page data using string (not recommended):
// in template data function
props: {
dataImage: '/static/images/mock-test.jpg';
}
props: { dataImage: '/static/images/mock-test.jpg' }
- from your mock data file or story file inside the component folder:
// top of file
import mockTest from './mocks/mock-test.jpg';
// in template data function
props: {
dataImage: mockTest;
}
publicPath
If you specify a different publicPath
through the --public-path
argument when running npm run build
, you should prepend process.
to all of your assets paths.
string paths
To work properly during dev and preview builds, when using string paths you need to use the publicPath
(starting with /
), which doesn't work when deploying storybook in sub folders.
Preview only
These assets will only be copied over to the dist
folder when running npm run build
, and are not part of the build that will be integrated into the CMS.
Examples
Loading an image inside your template.
// src/pages/image-test.ts
import type { AppTemplateProps } from '../App.template';
import mockTest from './static/images/mock-test.jpg';
export const data = (): AppTemplateProps => ({
layout: {
name: 'layout-default',
props: {
blocks: [
{
name: 'image-test',
props: {
dataImage: mockTest,
},
},
],
},
},
});
// src/components/atoms/image-test/ImageTest.template.ts
export type ImageTestProps = {
dataImage?: string;
};
export function imageTestTemplate({ dataImage }: ImageTestProps, ref) {
return html`<div data-component="image-test">
<div>
<h1>Data Image</h1>
<img src=${dataImage} />
</div>
</div>`;
}
Or using per-component mock files
// src/pages/image-test.ts
import type { AppTemplateProps } from '../App.template';
// import from the component folder to be reusable
import { imageTestDefaultMockData } from '../components/atoms/image-test/ImageTest.mocks';
export const data = (): AppTemplateProps => ({
layout: {
name: 'layout-default',
props: {
blocks: [
{
name: 'image-test',
props: imageTestDefaultMockData,
},
],
},
},
});
// src/components/atoms/image-test/ImageTest.mocks.ts
import { ImageTestProps } from './ImageTest.template';
// loading the file inside the component folder
// using a dedicated mock folder to make clear it's just for mocking
import mockComponentTest from './mocks/mock-component-test.jpg';
export const imageTestDefaultMockData: ImageTestProps = {
dataImage: mockComponentTest,
};
The mock file can also be used in stories.
// src/components/atoms/image-test/ImageTest.stories.ts
import { imageTestDefaultMockData } from './ImageTest.mocks';
// local asset
import mockComponentTest from './mocks/mock-component-test.jpg';
// global assets (not ideal, but fine to do)
import mockTest from '../../../pages/static/images/mock-test.jpg';
export const Default: Story<ImageTestProps> = () => ({
component: ImageTest,
template: imageTestTemplate as any,
});
// local asset, using webpack require
Default.args = {
dataImage: mockComponentTest,
};
// local asset, reusing the same mock data
Default.args = imageTestDefaultMockData;
// local asset, using path (not recommended, not possible)
Default.args = {
dataImage: '' /* not possible */,
};
// global assets, using webpack require (not ideal, but fine to do)
Default.args = {
dataImage: mockTest,
};
// global assets, using string path (not recommended)
Default.args = {
dataImage: '/static/images/mock-test.jpg',
};