
Security News
PolinRider: North Korea-Linked Supply Chain Campaign Expands Across Open Source Ecosystems
PolinRider expands across npm, Packagist, Go modules, and Chrome extensions, using hidden loaders to target developer environments.
@shadow-js/core
Advanced tools
The core ShadowJS framework providing fine-grained reactivity, JSX runtime, and essential UI components.
npm install @shadow-js/core
For development with hot reload and JSX compilation:
npm install @shadow-js/core @shadow-js/vite
import { useStore, Show, For } from "@shadow-js/core";
function Counter() {
const [count, setCount] = useStore(0);
const [items, setItems] = useStore(["Shadow", "JS", "Reactivity"]);
return (
<div>
<h2>Counter: {() => count()}</h2>
<button onClick={() => setCount(count() + 1)}>Increment</button>
<Show when={() => count() > 5} fallback={<p>Keep clicking!</p>}>
<p>🎉 You reached {() => count()}!</p>
</Show>
<ul>
<For each={() => items()}>{(item) => <li>{() => item}</li>}</For>
</ul>
</div>
);
}
Create reactive state that automatically updates dependent computations:
import { useStore } from "@shadow-js/core";
function App() {
const [count, setCount] = useStore(0);
const doubled = () => count() * 2; // Reactive computation
return (
<div>
<p>Count: {() => count()}</p>
<p>Doubled: {() => doubled()}</p> {/* Updates automatically */}
<button onClick={() => setCount(count() + 1)}>+</button>
</div>
);
}
import { useEffect, onMount, onCleanup } from "@shadow-js/core";
function Timer() {
const [time, setTime] = useStore(new Date());
onMount(() => {
console.log("Component mounted");
});
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date());
}, 1000);
// Cleanup on unmount or dependency change
return () => clearInterval(interval);
});
onCleanup(() => {
console.log("Component unmounting");
});
return <div>Current time: {() => time().toLocaleTimeString()}</div>;
}
import { Show } from "@shadow-js/core";
function UserStatus({ user }) {
return (
<Show when={() => user()} fallback={<div>Please log in</div>}>
<div>Welcome back, {() => user().name}!</div>
</Show>
);
}
import { For } from "@shadow-js/core";
function TodoList({ todos }) {
return (
<ul>
<For each={() => todos()}>
{(todo, index) => (
<li>
{() => index() + 1}. {() => todo.title}
</li>
)}
</For>
</ul>
);
}
import { Switch, Match } from "@shadow-js/core";
function StatusDisplay({ status }) {
return (
<Switch fallback={<div>Unknown status</div>}>
<Match when={() => status() === "loading"}>
<div>Loading...</div>
</Match>
<Match when={() => status() === "success"}>
<div>Success!</div>
</Match>
<Match when={() => status() === "error"}>
<div>Error occurred</div>
</Match>
</Switch>
);
}
import { Suspense, lazy } from "@shadow-js/core";
// Lazy load components
const LazyComponent = lazy(() => import("./HeavyComponent"));
function App() {
return (
<Suspense fallback={<div>Loading component...</div>}>
<LazyComponent />
</Suspense>
);
}
| Hook | Description |
|---|---|
useStore<T>(initialValue) | Create a reactive store |
useEffect(fn, deps?) | Run side effects when dependencies change |
useMemo<T>(fn, deps?) | Memoize expensive computations |
useId() | Generate unique IDs |
onMount(fn) | Run code when component mounts |
onCleanup(fn) | Run cleanup code when component unmounts |
| Component | Description |
|---|---|
<Show> | Conditional rendering with fallback |
<For> | Render lists with automatic key management |
<Switch> | Multiple conditional rendering |
<Match> | Case condition for Switch |
<Suspense> | Handle async operations |
<ErrorBoundary> | Catch and handle errors |
| Utility | Description |
|---|---|
createContext<T>() | Create a context for dependency injection |
useContext<T>(context) | Consume context values |
lazy<T>(importFn) | Lazy load components |
render(component, container) | Render component to DOM |
Fragment | Group elements without wrapper |
import { useStore, useEffect } from "@shadow-js/core";
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useStore<T>(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
useEffect(() => {
try {
localStorage.setItem(key, JSON.stringify(storedValue()));
} catch (error) {
console.warn(`Error saving to localStorage:`, error);
}
});
return [storedValue, setStoredValue] as const;
}
import { createContext, useContext } from "@shadow-js/core";
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useStore<"light" | "dark">("light");
const toggleTheme = () => {
setTheme(theme() === "light" ? "dark" : "light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within ThemeProvider");
}
return context;
}
import { ErrorBoundary } from "@shadow-js/core";
function App() {
return (
<ErrorBoundary fallback={(error) => <div>Error: {error.message}</div>}>
<ComponentThatMightError />
</ErrorBoundary>
);
}
For the best development experience, use the ShadowJS Vite plugin:
vite.config.ts
import { defineConfig } from "vite";
import shadow from "@shadow-js/vite";
export default defineConfig({
plugins: [shadow()],
});
If you prefer manual setup, configure your bundler to use ShadowJS JSX runtime:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@shadow-js/core"
}
}
ShadowJS works with any CSS solution:
// Inline styles (reactive)
<div style={() => ({ color: theme() === "dark" ? "white" : "black" })}>
Dynamic styling
</div>
// CSS Classes
<div className="my-component">
Static styling
</div>
// CSS Modules
import styles from "./Component.module.css";
<div className={styles.container}>CSS Modules</div>
ShadowJS is built with performance and developer experience in mind:
Check out the examples documentation for comprehensive code examples covering:
We welcome contributions! See the Contributing Guide for details.
MIT License - see LICENSE for details.
Built by Jehaad AL-Johani for fine-grained reactivity.
FAQs
ShadowJS core reactivity and DOM runtime
The npm package @shadow-js/core receives a total of 2 weekly downloads. As such, @shadow-js/core popularity was classified as not popular.
We found that @shadow-js/core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
PolinRider expands across npm, Packagist, Go modules, and Chrome extensions, using hidden loaders to target developer environments.

Security News
Open source attacks are accelerating as AI coding agents pull in dependencies faster, with less human review.

Research
/Security News
Malicious Chrome and Firefox extensions posed as free VPNs while stealing clipboard data through later extension updates.