Shopify Vue Theme
This package helps you to use Vuejs, Scss in development and build into javascript, css files to integrate into Shopify theme.
Benefit
-
The limitation of Shopify theme structure source code is that it is difficult to manage when your project grows larger because you cannot customize the folder hierarchy according to each category or function that you want to group. With SVueTheme you can do that comfortably, as you only need to care about the javascript or css files that SVueTheme exports for use in your Shopify theme.
-
With VanilaJs, you have to set up and define everything from scratch, while using SVueTheme you develop entirely on Vuejs, so it's easy to reuse the features provided by Vue, as well as use external services and plugins easily by integrating into Vue.
-
With CSS, we also allow publishing from SCSS separately from Javascript. It's convenient when you can use all the features that SCSS brings.
Get started
Installation
npm | npm i -D svuet |
yarn | yarn add -D svuet |
Setup
- Create
svuet.config.js
file and export the following required object:
store : Your store name, this field is required if you want to run the shopify theme dev command | String | vue-stheme , vue-stheme.myshopify.com | - |
build.prefix : The filename prefix is output after the build.Set null if no prefix is desired. | String | svuet | svuet |
build.outdir : Folder containing files exported after build | String | assets | assets |
build.javascript : Script configuration information | Object | View Script configuration information | - |
build.styles : Style configuration information | Object | View Style configuration information | - |
Script configuration information
input : Input files | String | Object | resources/main.ts , {main:'resources/main.ts'} | - |
output : Output files | String | [name].[ext] | [name].js |
useLiquid : Is the output file in liquid format? | Bool | - | true |
alias : Alias path | Object | {'@':'resources'} | - |
Style configuration information
input : Input files | String | Object | resources/styles/main.scss , {main:'resources/styles/main.scss'} | - |
output : Output files | String | [name].[ext] | [name].css |
useLiquid : Is the output file in liquid format? | Bool | - | true |
alias : Alias path | Object | {'@styles':'resources/styles'} | - |
Config Example
View Config
- Add command to
package.json
file:
- Use the command
npm init -y
to create a package.json
file if your project doesn't have one yet.
- Add the script as follows:
{
"type": "module",
"scripts": {
"dev": "svuet dev",
"build": "svuet build"
}
}
Introduction
This is a step-by-step guide to using the Shopify theme folder structure.
Init app
svuet.config.js
: Follow the example file
# components/HelloWorld.vue
<template>
<div>Hello World!</div>
</template>
# resources/main.ts
import { bootstrap } from 'svuet';
import HelloWorld from './instances/HelloWorld.vue';
bootstrap((app) => {
app.instance('hello-world', HelloWorld);
});
# layouts/theme.liquid
<header>
<script src="{{ 'svuet-script.js' | asset_url }}" defer></script>
</header>
- You can now use the instances defined in the
resources/main.ts
file normally as HTML.
# sections/main-page.liquid
<hello-world></hello-world>
- Custom state pseudo-class CSS selectors
hello-world:state(creating) {
}
hello-world:state(mounted) {
}
Instance
# resources/main.ts
import { bootstrap, defineElementInput } from 'svuet';
bootstrap((app) => {
app.instance('hello-world', HelloWorld);
app.instance('hello-world', defineElementInput({original: HelloWorld}));
});
Config Provider Element
Use the <app-config-provider>
element to set configuration, set configuration values by setting values for the dataset (using the attribute prefixed with data-
) of this element.
<app-config-provider data-template="{{ template }}"></app-config-provider>
You can now use the config, $configs
info variable in the <template>
of your vue file or instance's slot.
Or you can also inject appConfigProvider
variable into vue file.
# components/HelloWorld.vue
<template>
<div>
<p>
Template from globalProperties value: {{ $configs.template }}
</p>
<p>
Template from provider value: {{ configs.template }}
</p>
</div>
</template>
<script setup>
const configs = inject('appConfigProvider');
</script>
# sections/main-page.liquid
<hello-world>
<template v-slot>
<p>
Template from globalProperties value: {% echo '{{ $configs.template }}'%}
</p>
</template>
</hello-world>
You can also set additional configuration in the bootstrap callback function.
import { bootstrap } from 'svuet';
bootstrap((app) => {
app.useConfig({name: 'VueApp'});
});
Component
# sections/main-page.liquid
<hello-world>
<hello-inner-component></hello-inner-component>
</hello-world>
# resources/main.ts
import { bootstrap, defineElementInput } from 'svuet';
import HelloWorld from './instances/HelloWorld.vue';
import HelloInnerComponent from './components/HelloInnerComponent.vue';
bootstrap((app) => {
app.component('hello-inner-component', HelloWorld);
app.components([
{ name: 'hello-inner-component', component: HelloWorld },
])
app.instance('hello-world', defineElementInput({
original: HelloWorld,
components: { HelloInnerComponent }
}));
});
Props
# sections/main-page.liquid
<hello-world v-prop:page-title="'{{ page.title }}'">
<template v-slot>
<p>
Page title prop value: {% echo '{{ pageTitle }}'%}
</p>
</template>
</hello-world>
- Note: that we have to use the
'
character to represent the prop as a string because it has to comply with Vue syntax. If you want to pass more complex props, you can use prop slots.
# sections/main-page.liquid
<hello-world v-prop:page-title="'{{ page.title }}'" v-prop:cart-item.#temp>
<template v-slot>
<p>
Page title prop value: {% echo '{{ pageTitle }}'%}
</p>
<p>
Cart first item title prop value: {% echo '{{ cartItem?.title }}'%}
</p>
</template>
<template v-slot:bind:cart-item>{{ cart.items | first | json }}</template>
</hello-world>
- You can also specify the exact data type by adding the suffix
.{type}
in the slot prop name, the values of type include: obj
, string
, num
, bool
, and default is obj
.
# sections/main-page.liquid
<hello-world v-prop:cart-item.#temp v-prop:cart-note.#temp>
<template v-slot:bind:cart-item.obj>{{ cart.items | first | json }}</template>
<template v-slot:bind:cart-note.string>{{ cart.note }}</template>
</hello-world>
- Pass prop in bootstrap callback function:
# resources/main.ts
import { bootstrap, defineElementInput } from 'svuet';
import HelloWorld from './instances/HelloWorld.vue';
bootstrap((app) => {
app.instance('hello-world', defineElementInput({
original: HelloWorld,
props: {
title: 'Bootstrap title prop'
}
}));
});
Plugin
# resources/plugins/dayjs.ts
import { Plugin } from "vue";
import dayjs from "dayjs";
const dayjsPlugin: Plugin = {
install: (app, options) => {
app.config.globalProperties.$dayjs = dayjs;
}
}
export default dayjsPlugin;
# resources/main.ts
import { bootstrap, defineElementInput } from 'svuet';
import dayjsPlugin from './plugins/dayjs.ts';
import HelloWorld from './instances/HelloWorld.vue';
bootstrap((app) => {
app.use(dayjsPlugin, );
app.instance('hello-world', defineElementInput({
original: HelloWorld,
plugins: [
{ plugin: dayjsPlugin, }
]
}));
});
Slots
About slots using VueJS syntax
# sections/main-page.liquid
<hello-world>
<template v-slot>This is default slot.</template>
<!--
Tags outside the <template> tag are in the default slot.
-->
<template v-slot:detail>This is detail slot.</template>
<template v-slot:counter="data">This is counter slot with prop data. Count: {% echo '{{ data.count }}' %}</template>
</hello-world>
# components/HelloWorld.vue
<template>
<div>
<slot></slot>
<slot name="detail"></slot>
<slot name="counter" :count="1"></slot>
</div>
</template>
Note: You cannot use the props slot as a normal slot.
💬 Contributions & Feedback
I deeply value contributions and feedback from the community to make this package even better. If you have ideas, issues to discuss, or improvements, feel free to create an issue or submit a pull request. Every contribution is greatly appreciated!
⭐ Show Your Support
If you find this package helpful, please consider giving it a ⭐ on GitHub. It’s a small gesture that motivates me to keep creating and improving open-source projects. Thank you so much! ❤️