UtopiaGuidesIntegrating with JavaScript

Integrating with JavaScript

This guide explains how to integrate JavaScript into your Utopia application.

Using Import Maps

Import maps provide a modern way to manage JavaScript module dependencies. Utopia includes built-in support for import maps through the class Utopia::ImportMap class.

Installing JavaScript Libraries

First, install the library using npm:

$ npm install jquery

Copy the distribution files to public/_components:

$ bundle exec bake utopia:node:update

This will copy the library's distribution files (typically from node_modules/*/dist/) to your public/_components/ directory, making them available for local serving.

Creating the Import Map

Create a global import map in lib/my_website/import_map.rb:

require "utopia/import_map"

module MyWebsite
	IMPORT_MAP = Utopia::ImportMap.build(base: "/_components/") do |map|
		map.import("jquery", "./jquery/jquery.js")
	end
end

Then load this in lib/my_website.rb:

require_relative "my_website/import_map"

Adding to Your Pages

Add it to your page template (pages/_page.xnode), using relative_to to adjust paths for the current page:

<html>
	<head>
		#{MyWebsite::IMPORT_MAP.relative_to(request.path + "/")}
	</head>
	<body>
		<!-- Your content -->
	</body>
</html>

Using the Library

Once the import map is set up, you can import and use the library in your scripts:

<script type="module">
	// <![CDATA[
	import $ from 'jquery';
	
	$(document).ready(function() {
		console.log("jQuery is ready!");
	});
	// ]]>
</script>

Advanced Import Map Features

Using CDN URLs

Import maps support direct CDN imports without downloading files:

IMPORT_MAP = Utopia::ImportMap.build do |map|
	map.import("react", "https://esm.sh/react@18")
	map.import("vue", "https://cdn.jsdelivr.net/npm/vue@3/dist/vue.esm-browser.js")
end

Nested Base URLs

You can organize imports from different sources using nested with(base:) blocks:

IMPORT_MAP = Utopia::ImportMap.build do |map|
	# Local components
	map.with(base: "/_components/") do |local|
		local.import "app", "./app.js"
	end
	
	# CDN imports
	map.with(base: "https://cdn.jsdelivr.net/npm/") do |cdn|
		cdn.import "lit", "lit@2.7.5/index.js"
		cdn.import "lit/decorators.js", "lit@2.7.5/decorators.js"
	end
end

Subresource Integrity

Add integrity hashes for enhanced security:

IMPORT_MAP = Utopia::ImportMap.build do |map|
	map.import("react", "https://esm.sh/react@18", integrity: "sha384-...")
end

Scoped Imports

Use scopes to resolve imports differently based on the referrer URL (the page or module location where the import is being made):

IMPORT_MAP = Utopia::ImportMap.build do |map|
	map.import("utils", "/utils.js")
	
	# When importing from any page under /admin/, use a different utils module
	map.scope("/admin/", {"utils" => "/admin/utils.js"})
end

When you're on a page at /admin/dashboard and you import "utils", it will resolve to /admin/utils.js. On other pages, it resolves to /utils.js.

Traditional JavaScript

You can also use JavaScript by embedding it directly into your HTML, or by creating a JavaScript source file and referencing that.

Embedding Code

When embedding JavaScript directly in XRB templates, wrap the code in CDATA comments to prevent XRB's parser from interpreting special characters like <, >, and &:

<html>
	<body>
		<script type="text/javascript">
			// <![CDATA[
			console.log("Hello World")
			// ]]>
		</script>
	</body>
</html>

External Script

In script.js:

console.log("Hello World")

In your HTML view:

<html>
	<body>
		<script type="text/javascript" src="script.js"></script>
	</body>
</html>