Building your user interface with MUI using Preact and HTM without the need for a setup process

Preact showcases its compatibility with HTM for a no-build setup, which has been working seamlessly on my phone as Vite was causing issues due to symlinks. Learn more here.

MUI provides guidance on how to install and use it with Preact, although not specifically with HTM for a no-build setup. Find the details at https://mui.com/material-ui/getting-started/

I attempted to import using

import { Button } from '@mui/material/Button';

I tried importing without brackets or directly importing the file like this

import { Button } from '../node_modules/@mui/material/Button.js';

However, the console still displays...

material-ui.development.js:1848 Uncaught TypeError: Cannot read properties of undefined (reading 'useLayoutEffect')
at material-ui.development.js:1848:74
...and several similar error messages.

How can I make this work with both Preact and htm for a no-build setup?

Below is my code snippet...

My package.json:

{
  "name": "project",
  "private": true,
  "version": "0.001",
  "type": "module",
  "scripts": {},
  "dependencies": {
    "@emotion/react": "^11.11.4",
    "@emotion/styled": "^11.11.5",
    "@mui/material": "^5.15.19",
    "@testing-library/jest-dom": "latest",
    "@testing-library/react": "latest",
    "@testing-library/user-event": "latest",
    "htm": "^3.1.1",
    "preact": "^10.22.0",
    "react-scripts": "^5.0.0"
  }
}

My index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Site title</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, interactive-widget=resizes-content">
    <script src="js/standalone.module.js" type="module"></script>
  </head>
  <body>
    <div id="root"></div>

    <script type="module" src="components/App.js"></script>
  </body>
</html>

My App.js:

import { html, render } from '../js/standalone.module.js';
import { Button } from '@mui/material/Button';

function ButtonUsage() {
  return html`
    <${Button} variant="contained">Hello world<//>
  `;
}

render(html`<${ButtonUsage} />`, document.getElementById('root'));

Answer №1

Before we dive in, it's important to note that react-scripts, with the help of Babel, serves as a build tool. This could be causing some confusion in your query. It's not typically used in a setup that doesn't involve building processes.


When using a bare import specifier like @mui/material/Button, it's designed for Node or more recently, for use with import maps. On its own, this type of specifier is considered invalid for web usage.

In addition, since MUI is a React library, you'll need to configure aliases to point to preact/compat. Without these configurations, it won't function seamlessly in a Preact application (as indicated by your errors).

Here's an example:

Note: It's likely that js/standalone.module.js should actually be htm/preact/standalone, which would be a more appropriate form when utilizing import maps.

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Site title</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, interactive-widget=resizes-content">
    <script type="importmap">
      {
        "imports": {
          "preact": "https://esm.sh/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="dcacaeb9bdbfa89cedecf2ede5f2ee">[email protected]</a>",
          "preact/": "https://esm.sh/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="136361767270675322233d222a3d21">[email protected]</a>/",
          "htm/preact": "https://esm.sh/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="8ce4f8e1ccbfa2bda2bd">[email protected]</a>/preact?external=preact",
          "@mui/material": "https://esm.sh/@mui/material?alias=react:preact/compat,react-dom:preact/compat&external=preact"
        }
      }
    </script>
  </head>
  <body>
    <div id="root"></div>

    <script type="module" src="components/App.js"></script>
  </body>
</html>

Next, within your script:

import { render } from 'preact';
import { html } from 'htm/preact';
import { Button } from '@mui/material';

function ButtonUsage() {
  return html`
    <${Button} variant="contained">Hello world<//>
  `;
}

render(html`<${ButtonUsage} />`, document.getElementById('root'));

Hopefully, this clarifies things for you!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Engage with Vue.js and traditional JavaScript (plain) in your projects

Exploring the integration of Vue with regular JavaScript has presented a challenge. While some solutions online propose moving all functions to a Vue component, this approach proves impractical for complex or large functions. The task now is finding a way ...

What are the best practices for sharing context in express and typescript?

Implementing a solution to expose a value to all request handlers using express and typescript is my goal. I am looking for a way to easily "inject" this value from a middleware or an alternative method that allows for simple mocking if needed. Here is th ...

How to stop Bootstrap collapsible items from "shifting"

I recently integrated a Bootstrap collapse plugin to manage my list of common inquiries: https://jsfiddle.net/2d8ytuq0/ <ul class="faq__questions"> <li> <a href="#" data-toggle="collapse" data-target="#faq__question_1">..</a> ...

Guide to triggering React Material-UI modal and filling it with data from an Ajax request when a button is clicked

Despite my efforts to find a similar question, I couldn't come across one. My apologies if I overlooked it. Currently, I am working on a React Material-UI project to develop a basic web application. Within this application, there is an XGrid that disp ...

JQuery is blocking the submission of an HTML form

During my exploration of an AJAX/JQuery tutorial for a registration script that interacts with PHP/MySQL and is submitted via JQuery, I encountered a recurring issue. The problem lies in the form submitting directly to the action page instead of its intend ...

Patience is key when waiting for both subscriptions to be completed before proceeding with an

I am facing a challenge with two subscriptions as shown below: this.birthdays = await this.birthdaySP.getBirthdays(); this.birthdays.subscribe(groups => { const allBirthdayT = []; groups.map(c => { allBirthdayT.push({ key: c.pa ...

I'm getting errors from TypeScript when trying to use pnpm - what's going

I've been facing an issue while attempting to transition from yarn to pnpm. I haven't experimented with changing the hoisting settings yet, as I'd prefer not to do so if possible. The problem lies in my lack of understanding about why this m ...

Ways to reload a webpage from the bottom without any animation

It seems common knowledge that refreshing a webpage on mobile devices can be done by pulling down from the top. However, I am looking to shake things up and refresh the page by pulling up from the bottom instead. And I prefer no animations in the process. ...

React JS error: Trying to use props.video as a function, but it's not

As a newcomer to React, I'm encountering some errors that I need help debugging. Error #1: Uncaught TypeError: props.videos.map is not a function at new VideoList Error #2: bundle.js:19956 Error: findComponentRoot(..., .0.0.0): Unable to find el ...

Redirect middleware for Next.js

I am currently working on implementing a log-in/log-out feature in my project using nextjs, redux-saga, and mui libraries. // middleware.ts import { NextRequest, NextResponse } from 'next/server'; import { RequestCookies } from 'next/dist/c ...

Converting a JavaScript array to formData

Struggling with sending an array via Ajax to PHP. For more information, visit jsfiddle https://jsfiddle.net/CraigDavison/9spyn7ah/ function autoPopulate() { var cast = "John Wayne, Julia Roberts, Kevin Spacey"; cast = cast.split(', '); ...

How to get the most out of the $scope variable?

Is it possible to assign a regular JavaScript variable the current value of an Angular $scope variable without having their values binded together? //$scope.var1 is set to a specific value, for example, 5 var v2 = $scope.var1; //$scope.var1 is then update ...

Click here to get the button click link

Is there a method to obtain the link of a button click on a website? This is the Website and it features a play button. I am looking for a way to capture the link triggered by clicking the play button so that the audio starts playing automatically each t ...

Customize the font color in Material UI to make it uniquely yours

How can I customize the default Text Color in my Material UI Theme? Using primary, secondary, and error settings are effective const styles = { a: 'red', b: 'green', ... }; createMuiTheme({ palette: { primary: { ...

What tool can be used for formatting and syntax highlighting when working with ejs (embedded javascript) templates?

When working on my project, I utilize EJS as the express templating engine. While it is user-friendly and efficient, I have encountered difficulties when it comes to editing files - the text highlighting could be better, and I have not been able to find an ...

Adjust width based on value dynamically

I currently have a div that is sized at 250px, housing 3 child divs within it. My goal is for each of these 3 divs to dynamically scale based on their respective values, eventually reaching 100% width. This is the code snippet I am working with: <di ...

Transform a PNG image with transparency into a JPEG file using imagemagick

Consider utilizing the imagemagick npm module for your image manipulation needs. In the task of converting a .png file with a transparent background to a .jpeg with a white background, you may encounter challenges. Here is an example: const ImageMagick ...

Simulate a new Date object in Deno for testing purposes

Has anyone successfully implemented something similar to jest.spyOn(global, 'Date').mockImplementation(() => now); in Deno? I've searched through the Deno documentation for mock functionality available at this link, as well as explored t ...

The appropriate method for transferring a prototype to an object

From my perspective, prototypes work like this: let Animal = function() { this.bark = "woof"; } Animal.prototype.barkLoud = function() { return this.bark.toUpperCase(); } let x = new Animal(); x.barkLoud() = "WOOF"; I f ...

Is there a way to incorporate thumbnails into the search results displayed in the Add Product modal within the Woocommerce Admin interface?

https://i.sstatic.net/ZCD0s.png I am exploring ways to enhance the search functionality in the Add Products modal within the WooCommerce Admin interface. My goal is to showcase the thumbnail for each product in the search results along with other product ...