Skip to content

Custom Vue.js project

Custom Vue.js project

Follow these steps to integrate Shopware Frontends into an existing, custom Vue.js project

  • Install the required dependencies
  • Prepare a Vue plugin for better encapsulation
  • Configure the API client and create application instance
  • Store and handle client state

Creating Vue project

INFO

You can skip this part if you have an existing project.

bash
pnpm create vue@latest

More information about creating a new Vue project can be found here

Install dependencies

First of all, install the required npm dependencies:

bash
pnpm add @shopware-pwa/composables-next @shopware-pwa/api-client

If you are using TypeScript in you application, you may want to have another package that can be installed for dev dependencies:

bash
pnpm add -D @shopware-pwa/types

Additionally, to keep the current session context even after page reloads, we are going to install a cookie helper to set and get value of context token in our plugin:

bash
pnpm add js-cookie

For CMS components, you can add a package that contains ready-to-use components. You can read more about CMS pages here:

Create content pages
Render a content page using components
bash
pnpm add @shopware-pwa/cms-base

Configure API client

Code example

Find a full example of the Vue.js plugin here.

Now, let's configure the API client and business logic together.

INFO

The business logic is written to be Vue 3 compatible. Under the hood, it utilizes the composition API, especially the provide/inject feature for sharing state.

In order to configure the business logic and API client together with your Vue 3 application, it's required to create a Shopware instance provided by a factory method within the @shopware-pwa/composables-next package. Everything will be encapsulated in a plugin and installed later on.

Vue plugins

This section requires having knowledge about the concept of Vue 3 plugins.

Import necessary methods from @shopware-pwa/api-client, @shopware-pwa/composables-next and js-cookie packages:

ts
// ./plugins/vue-shopware-frontends.ts file
import { ref } from "vue";
import type { App } from "vue";
import { createInstance } from "@shopware-pwa/api-client";
import { createShopwareContext } from "@shopware-pwa/composables-next";
import Cookies from "js-cookie";

export default {
  install: (app: App, options: ShopwareFrontendsOptions) => {
    ...
  },
};

We prepare some types to be used during the registration of the plugin to pass basic credentials for your Shopware 6 instance.

ts
export type ShopwareFrontendsOptions = {
  shopwareEndpoint: string;
  shopwareAccessToken: string;
  shopwareApiClient?: {
    timeout: number;
  };
  enableDevtools?: boolean;
};

Now, once the plugin is created, we need to create an API client instance and the Shopware instance for Vue application.

The install method is a good place to do that:

ts
const cookieContextToken = Cookies.get("sw-context-token");
const cookieLanguageId = Cookies.get("sw-language-id");

const contextToken = ref(cookieContextToken);
const languageId = ref(cookieLanguageId);

const instance = createInstance({
  endpoint: options.shopwareEndpoint,
  accessToken: options.shopwareAccessToken,
  timeout: options.shopwareApiClient?.timeout || 5000,
  contextToken: contextToken.value,
  languageId: languageId.value,
});

Handle client state

Code example

Complete code example can be found HERE you can find a full example of the plugin

Now, we need to ensure that the context token, which identifies a user session, is properly stored and updated. The context token may change after operations like login or logout.

Then, we can take advantage of the onConfigChange method. It executes when the API client detects a new value of the context token coming from the API (as a header parameter or in the response body). In that case, the new context token should be saved in the cookie to keep the correct session:

ts
/**
 * Save current contextToken when it changes
 */
instance.onConfigChange(({ config }) => {
  try {
    Cookies.set("sw-context-token", config.contextToken || "", {
      expires: 365,
      sameSite: "Lax",
      path: "/",
    });
    Cookies.set("sw-language-id", config.languageId || "", {
      expires: 365,
      sameSite: "Lax",
      path: "/",
    });

    contextToken.value = config.contextToken;
    languageId.value = config.languageId;
  } catch (e) {
    // Sometimes cookie is set on server after request is send, it can fail silently
  }
});

Another step is to create a Shopware instance that combines API Client and the business logic in composables to be used in entire Vue application:

ts
const shopwareContext = createShopwareContext(app, {
  apiInstance: instance, // pass API Client instance
  enableDevtools: !!options.enableDevtools, // decide if devtools should be enabled
});

And the last step is to provide the shopwareContext:

ts
app.provide("shopware", shopwareContext);
// thanks to this, `shopwareContext` can be injected in a component and other Vue-instance-aware places (like composables).
app.provide("swSessionContext", ref());

Register the plugin

ts
// main.ts
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
// import previously implemented module
import ShopwareFrontends from "./plugins/vue-shopware-frontends";
const app = createApp(App);

app.use(ShopwareFrontends, {
    // pass options described under ShopwareFrontendsOptions type in the previous section
    shopwareEndpoint: "https://demo-frontends.swstage.store",
    shopwareAccessToken: "SWSCBHFSNTVMAWNZDNFKSHLAYW",
    apiDefaults: {},
});

app.mount("#app");

Plugin code

ts
// ./plugins/vue-shopware-frontends.ts file
import { ref } from "vue";
import type { App } from "vue";
import { createInstance } from "@shopware-pwa/api-client";
import { createShopwareContext } from "@shopware-pwa/composables-next";
import Cookies from "js-cookie";

// Types to be used during the registration of the plugin to pass basic credentials for your Shopware 6 instance.
export type ShopwareFrontendsOptions = {
  shopwareEndpoint: string;
  shopwareAccessToken: string;
  shopwareApiClient?: {
    timeout: number;
  };
  enableDevtools?: boolean;
};

export default {
  install: (app: App, options: ShopwareFrontendsOptions) => {
    const cookieContextToken = Cookies.get("sw-context-token");
    const cookieLanguageId = Cookies.get("sw-language-id");

    const contextToken = ref(cookieContextToken);
    const languageId = ref(cookieLanguageId);

    const instance = createInstance({
      endpoint: options.shopwareEndpoint,
      accessToken: options.shopwareAccessToken,
      timeout: options.shopwareApiClient?.timeout || 5000,
      contextToken: contextToken.value,
      languageId: languageId.value,
    });

    instance.onConfigChange(({ config }) => {
      try {
        Cookies.set("sw-context-token", config.contextToken || "", {
          expires: 365,
          sameSite: "Lax",
          path: "/",
        });
        Cookies.set("sw-language-id", config.languageId || "", {
          expires: 365,
          sameSite: "Lax",
          path: "/",
        });

        contextToken.value = config.contextToken;
        languageId.value = config.languageId;
      } catch (e) {
        // Sometimes cookie is set on server after request is send, it can fail silently
      }
    });

    const shopwareContext = createShopwareContext(app, {
      apiInstance: instance,
      enableDevtools: !!options.enableDevtools,
    });

    app.provide("shopware", shopwareContext);
    app.provide("swSessionContext", ref());
  },
};

Shopware Endpoint on the SSR mode

It may happen that for SSR and CSR, you need two different shopware endpoints. One of the most common situations is when you are using an internal network for communication between apps.

Server URL to the backend: http://shopware (not exposed)
Client URL to the backend  https://demo-frontends.shopware.store (exposed)

If you are using the Nuxt plugin, you can set private and public envs:

NUXT_SHOPWARE_SHOPWARE_ENDPOINT=http://shopware
NUXT_PUBLIC_SHOPWARE_SHOPWARE_ENDPOINT=https://demo-frontends.shopware.store

Otherwise, make sure that you are setting different values on the create instance phase

ts
const instance = createInstance({
  endpoint: ssrValue || clientValue,
  accessToken: options.shopwareAccessToken,
  timeout: options.shopwareApiClient?.timeout || 5000,
  contextToken: contextToken.value,
  languageId: languageId.value,
});

Next steps

After your setup, you can follow our building guides to get started with Shopware Frontends

Getting Started - Navigation
Let's implement a store navigation