User Interface widgets and utilities for Solid (solid-ui)
These are HTML5 widgets which connect to a solid store. Building blocks for solid-based apps. Vanilla JS. Includes large widgets like chat, table, matrix, form fields, and small widgets.
See Solid-Ui Storybook for UI widgets. See Solid-UI API for UI functions. See Forms introduction for UI vocabulary implementation.
Contributions of bug fixes and new functionality, documentation, and tests are always appreciated.
npm install solid-ui rdflib solid-logicThen import in your JavaScript/TypeScript code:
import * as UI from 'solid-ui'
import * as $rdf from 'rdflib'
import * as SolidLogic from 'solid-logic'
// Example: Create a button
const button = UI.widgets.button(
document,
'https://solidproject.org/assets/img/solid-emblem.svg',
'Click me',
() => alert('Button clicked!')
)
document.body.appendChild(button)Solid-UI provides both UMD and ESM bundles for direct browser usage. Both bundles externalize rdflib and solid-logic, which must be loaded separately.
-
UMD (Universal Module Definition):
- Development:
dist/solid-ui.js(exposes globalwindow.UI) - Production:
dist/solid-ui.min.js(minified)
- Development:
-
ESM (ES Modules):
- Development:
dist/solid-ui.esm.js - Production:
dist/solid-ui.esm.min.js(minified)
- Development:
Load via <script> tags and access through global variables window.$rdf, window.SolidLogic, and window.UI.
<!DOCTYPE html>
<html>
<head>
<title>Solid-UI UMD Example</title>
</head>
<body>
<div id="app"></div>
<!-- Load dependencies first -->
<script src="https://cdn.jsdelivr.net/npm/rdflib/dist/rdflib.min.js"></script>
<script src="https://unpkg.com/solid-logic/dist/solid-logic.min.js"></script>
<!-- Load solid-ui UMD bundle -->
<script src="https://unpkg.com/solid-ui/dist/solid-ui.min.js"></script>
<script>
// Access via global variables
const { store, authn } = window.SolidLogic
const { widgets } = window.UI
// Get the logged-in user
const webId = authn.currentUser()
if (webId) {
// User is logged in - create button with their WebID
const userButton = widgets.button(
document,
'https://solidproject.org/assets/img/solid-emblem.svg',
`Logged in as: ${webId.value}`,
() => alert(`Your WebID: ${webId.value}`)
)
document.getElementById('app').appendChild(userButton)
} else {
// User not logged in - create login button
const loginButton = widgets.button(
document,
'https://solidproject.org/assets/img/solid-emblem.svg',
'Login to Solid',
() => authn.checkUser().then(() => location.reload())
)
document.getElementById('app').appendChild(loginButton)
}
</script>
</body>
</html>Use modern JavaScript modules with import statements.
<!DOCTYPE html>
<html>
<head>
<title>Solid-UI ESM Example</title>
</head>
<body>
<div id="app"></div>
<script type="module">
// Import from CDN (esm.sh, unpkg, or jsdelivr)
import * as $rdf from 'https://esm.sh/rdflib'
import * as SolidLogic from 'https://esm.sh/solid-logic'
import * as UI from 'https://esm.sh/solid-ui'
// Get the logged-in user
const webId = SolidLogic.authn.currentUser()
if (webId) {
// User is logged in - create personalized button
const userName = await getUserName(webId)
const userButton = UI.widgets.button(
document,
'https://solidproject.org/assets/img/solid-emblem.svg',
userName || 'My Profile',
() => window.open(webId.value, '_blank')
)
document.getElementById('app').appendChild(userButton)
} else {
// User not logged in
const loginButton = UI.widgets.button(
document,
'https://solidproject.org/assets/img/solid-emblem.svg',
'Login to Solid',
async () => {
await SolidLogic.authn.checkUser()
location.reload()
}
)
document.getElementById('app').appendChild(loginButton)
}
// Helper function to fetch user's name from their profile
async function getUserName(webId) {
try {
await SolidLogic.store.fetcher.load(webId.doc())
const name = SolidLogic.store.any(webId, $rdf.sym('http://xmlns.com/foaf/0.1/name'))
return name ? name.value : null
} catch (error) {
console.error('Error fetching user name:', error)
return null
}
}
</script>
</body>
</html>Use import maps for cleaner module specifiers:
<!DOCTYPE html>
<html>
<head>
<title>Solid-UI ESM with Import Maps</title>
</head>
<body>
<div id="app"></div>
<!-- Define import map for bare specifiers -->
<script type="importmap">
{
"imports": {
"rdflib": "https://esm.sh/rdflib",
"solid-logic": "https://esm.sh/solid-logic",
"solid-ui": "https://esm.sh/solid-ui"
}
}
</script>
<script type="module">
// Use clean bare specifiers
import * as $rdf from 'rdflib'
import * as SolidLogic from 'solid-logic'
import * as UI from 'solid-ui'
const app = document.getElementById('app')
// Create a profile button for logged-in user
async function createUserButton() {
const webId = SolidLogic.authn.currentUser()
if (!webId) {
const loginBtn = UI.widgets.button(
document,
'https://solidproject.org/assets/img/solid-emblem.svg',
'Login',
() => SolidLogic.authn.checkUser()
)
app.appendChild(loginBtn)
return
}
// Fetch user profile
try {
await SolidLogic.store.fetcher.load(webId.doc())
const name = SolidLogic.store.any(
webId,
$rdf.sym('http://xmlns.com/foaf/0.1/name')
)
const profileBtn = UI.widgets.button(
document,
'https://solidproject.org/assets/img/solid-emblem.svg',
name ? name.value : 'My Profile',
() => {
alert(`WebID: ${webId.value}\nName: ${name ? name.value : 'Not set'}`)
}
)
app.appendChild(profileBtn)
} catch (error) {
console.error('Error loading profile:', error)
}
}
createUserButton()
</script>
</body>
</html>When developing a component in solid-ui you can test it in isolation using storybook
npm run build
npm run storybook
If there is no story for the component yet, add a new one to ./src/stories.
When you want to test the component within a solid-pane, you can use the development mode of solid-panes.
One can run extisting tests with:
npm run test
or with coverage
npm run test-coverage
The following document gives guidance on how to add and perform testing in solid-ui. Testing in solid-ui
- The github pages should contain the storybook and further documentation. In order to make sure it is deployed there is a step in the CI (gh-pages). This depends on the previous
buildstep. It MUST containbuild-storybookotherwise the storybook is not being published.