# Can't build app when a custom template is passed to @customElement decorator

**URL:** <https://discourse.aurelia.io/t/cant-build-app-when-a-custom-template-is-passed-to-customelement-decorator/6355>\
**Category:** Help Requests\
**Created:** [February 17, 2025, 5:34pm UTC](https://discourse.aurelia.io/t/cant-build-app-when-a-custom-template-is-passed-to-customelement-decorator/6355 "2025-02-17T17:34:10Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![danjvarela](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/danjvarela/32/2584_2.png) [@danjvarela](https://discourse.aurelia.io/u/danjvarela)\
**Post date:** [February 17, 2025, 5:34pm UTC](https://discourse.aurelia.io/t/cant-build-app-when-a-custom-template-is-passed-to-customelement-decorator/6355/1 "2025-02-17T17:34:10Z")

</div>

Hello,

For some reason, importing a custom template and passing it into the `@customElement` decorator causes an error when running `npm run build`. There are no errors when running `npm start`.

I am using Typescript and Vite. You can refer to [this repository](https://github.com/danjvarela/custom-element-decorator-demo) for a minimal reproduction of the problem.

```ts
import { customElement } from "aurelia";
import template from "./my-app.html"; //<-----------

@customElement({ name: "my-app", template }) //<--------
export class MyApp {
  public message = "Hello World!";
}

```

I have made sure that there is no invalid syntax in the template. This is the error I get when building the app:

 ![image](https://canada1.discourse-cdn.com/flex027/uploads/aurelia/original/2X/e/ee9d2ee42b06014fe5129455bb545c320e9cb5b6.jpeg)

Please help. Thank you.

---

<div class="post-metadata">

**Author:** ![dwaynecharrington](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/dwaynecharrington/32/11_2.png) [@dwaynecharrington](https://discourse.aurelia.io/u/dwaynecharrington)\
**Post date:** [February 17, 2025, 10:59pm UTC](https://discourse.aurelia.io/t/cant-build-app-when-a-custom-template-is-passed-to-customelement-decorator/6355/2 "2025-02-17T22:59:36Z")

</div>

You need to use the `?raw` suffix on the import due to the way Vite treats imported resources. This will signal that the import should be imported as a string (which is what template expects): [Static Asset Handling | Vite](https://vite.dev/guide/assets#importing-asset-as-string) - I am going to update the docs to mention this too, as it is something that can catch a lot of devs offguard.

---

<div class="post-metadata">

**Author:** ![dwaynecharrington](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/dwaynecharrington/32/11_2.png) [@dwaynecharrington](https://discourse.aurelia.io/u/dwaynecharrington)\
**Post date:** [February 21, 2025, 4:46am UTC](https://discourse.aurelia.io/t/cant-build-app-when-a-custom-template-is-passed-to-customelement-decorator/6355/3 "2025-02-21T04:46:24Z")

</div>

To anyone else encountering this, I’ve got a PR up that handles this in the Aurelia Vite plugin so you don’t have to manually suffix `?raw` to HTML imports.

---

<div class="post-metadata">

**Author:** ![danjvarela](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/danjvarela/32/2584_2.png) [@danjvarela](https://discourse.aurelia.io/u/danjvarela)\
**Post date:** [February 21, 2025, 12:11pm UTC](https://discourse.aurelia.io/t/cant-build-app-when-a-custom-template-is-passed-to-customelement-decorator/6355/4 "2025-02-21T12:11:05Z")

</div>

Here’s what I came up with which works so far:

````ts
import { PluginOption } from 'vite';

type HtmlCssImportPatchOptions = {
  ignorePattern?: {
    css?: Array<string | RegExp>;
    html?: Array<string | RegExp>;
  };
};

/**
 * This adds `?inline` at the end of all css/scss imports
 * and `?raw` at the end of all html imports inside `.ts` files
 *
 * This is needed so we can supply custom template and style on the `@capElement` decorator
 *
 * @example
 *
 * ```ts
 * import style from './index.css'
 * // becomes
 * // import style from './index.css?inline'
 *
 * import template from './index.template'
 * // becomes
 * // import template from './index.html?raw'
 * ```
 */
export function htmlCssImportPatch(options: HtmlCssImportPatchOptions = {}): PluginOption {
  return {
    name: 'vite-plugin-html-css-import-patch',
    enforce: 'pre',
    transform(code, id) {
      if (id.endsWith('.ts')) {
        const shouldIgnore = (patterns: Array<string | RegExp> | undefined, path: string) => {
          return patterns?.some(pattern => (typeof pattern === 'string' ? path.includes(pattern) : pattern.test(path)));
        };

        const newCode = code
          .replace(/import\s+([^\s]+)\s+from\s+(['"])(.*?\.html)\2/g, (match, variable, quote, path) => {
            if (shouldIgnore(options.ignorePattern?.html, path)) return match;
            return `import ${variable} from ${quote}${path}?raw${quote}`;
          })
          .replace(/import\s+([^\s]+)\s+from\s+(['"])(.*?\.(?:css|scss))\2/g, (match, variable, quote, path) => {
            if (shouldIgnore(options.ignorePattern?.css, path)) return match;
            return `import ${variable} from ${quote}${path}?inline${quote}`;
          });

        return {
          code: newCode,
          map: null
        };
      }
      return null;
    }
  };
}

````

Then, in my vite.config.ts,

```ts
import { htmlCssImportPatch } from './plugins/html-css-import-patch';

export default defineConfig(({ mode }) => {
  return {
    //...other configs,
    plugins: [
      htmlCssImportPatch(),
      aurelia({
        useDev: true,
        defaultShadowOptions: { mode: 'open' }
      }) as PluginOption[],
      // ....other plugins here
    ]
  };
});

```

**NOTE** : In our project, we needed the `?inline` on css/scss imports so we can customize the styles as well. We have a `@customElement` decorator wrapper which looks like this:

```ts
import { type Constructable, type Key } from '@aurelia/kernel';
import { type PartialCustomElementDefinition } from '@aurelia/runtime-html';
import { customElement, ShadowDOMRegistry } from '@aurelia/runtime-html';

const shadowOptions = { mode: 'open' } as const;
export function xElement(
  name: string,
  def: Omit<PartialCustomElementDefinition, 'name'> & { style?: string },
  dependencies: Key[] = []
) {
  const { style, ..._def } = def;
  if (style) {
    dependencies.push(new ShadowDOMRegistry([style]));
  }

  return <T extends Constructable>(Type: T, context: ClassDecoratorContext) =>
    customElement({ ..._def, name, shadowOptions, dependencies })(Type, context);
}

```

Then, if we want to add a custom style and template to a component,

```ts
import { xElement } from 'lib/framework';
import template from './some-foo.html';
import style from './some-foo.css';

@xElement('some-foo', { template, style })
export class Foo {}

```
