@jamwidgets/astro
v0.6.1
Published
Astro components and content loaders for Jamwidgets
Maintainers
Readme
@jamwidgets/astro
Note: This repo is a read-only mirror. Source lives in a private monorepo. For issues/PRs, please open them here and we'll sync changes back.
Astro components and content loader for Jamwidgets - widgets for static sites.
Installation
npm install @jamwidgets/astroGalleries
---
import GalleryIndex from "@jamwidgets/astro/GalleryIndex";
import Gallery from "@jamwidgets/astro/Gallery";
---
<GalleryIndex siteKey="your-key" deepLink />
<Gallery siteKey="your-key" slug="field-notes" tag="portraits" />Setup
Add your Jamwidgets site key to your .env:
JAMWIDGETS_SITE_KEY=your_site_key_here
SITE_URL=https://example.comContent Loader (Posts)
Fetch posts from Jamwidgets at build time using Astro's content collections:
// src/content.config.ts
import { defineCollection } from "astro:content";
import { jamwidgetsPostsLoader } from "@jamwidgets/astro/loader";
const posts = defineCollection({
loader: jamwidgetsPostsLoader({
siteKey: import.meta.env.JAMWIDGETS_SITE_KEY,
origin: import.meta.env.SITE_URL,
}),
});
export const collections = { posts };Set origin to an allowed origin for production builds. Jamwidgets rejects the
request when the site restricts origins and the loader omits it.
Then use in your pages:
---
import { getCollection } from "astro:content";
const posts = await getCollection("posts");
---
{posts.map((post) => (
<article>
<h2>{post.data.title}</h2>
<p>{post.data.excerpt}</p>
</article>
))}Render a post's Markdown body with Astro's content component:
---
import { getEntry, render } from "astro:content";
import { Image } from "astro:assets";
const post = await getEntry("posts", "hello-world");
if (!post) throw new Error("Post not found");
const { Content } = await render(post);
---
{post.data.coverImage && (
<Image
src={post.data.coverImage}
inferSize
layout="full-width"
alt={post.data.title}
/>
)}
<article><Content /></article>post.data.content is Markdown, not an HTML string. Do not render it with
set:html; use Astro's render() and <Content /> as above.
For responsive uploaded images, authorize Jamwidgets' asset host in your Astro
config. Astro will then generate correctly sized image variants for Markdown
images. The hostname below is for Jamwidgets Cloud; self-hosted deployments use
the hostname configured by ASSET_BASE_URL (or fall back to
<AWS_S3_BUCKET>.s3.<AWS_S3_REGION>.amazonaws.com):
import { defineConfig } from "astro/config";
export default defineConfig({
image: {
domains: ["assets.jamwidgets.com"],
layout: "constrained",
responsiveStyles: true,
},
});Loader Options
jamwidgetsPostsLoader({
siteKey: string; // Required - your Jamwidgets site key
endpoint?: string; // Default: 'https://jamwidgets.com'
origin?: string; // Site URL used for allowed-origin validation
tag?: string; // Filter posts by tag
limit?: number; // Max posts to fetch (default: 500)
onError?: 'throw' | 'warn' | 'ignore'; // Error handling
})Components
Form
A wrapper component for contact forms with built-in spam protection:
---
import Form from "@jamwidgets/astro/Form";
---
<Form siteKey={import.meta.env.JAMWIDGETS_SITE_KEY} formSlug="contact">
<input name="name" placeholder="Name" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<button type="submit">Send</button>
</Form>Props:
siteKey(required) - Your Jamwidgets site keyformSlug(required) - The form slug as configured in Jamwidgetsendpoint- Base URL (default:https://jamwidgets.com)theme-'light'|'dark'|'auto'(default:'light')class- Additional CSS class
Events:
jamwidgets:loading- Form submission startedjamwidgets:success- Submission successful (detail contains response)jamwidgets:error- Submission failed (detail contains error)
Comments
Threaded comments with a submission form:
---
import Comments from "@jamwidgets/astro/Comments";
---
<Comments
siteKey={import.meta.env.JAMWIDGETS_SITE_KEY}
pageId={Astro.url.pathname}
/>Props:
siteKey(required) - Your Jamwidgets site keypageId(required) - Unique page identifier (e.g., URL path)endpoint- Base URL (default:https://jamwidgets.com)theme-'light'|'dark'|'auto'(default:'light')class- Additional CSS class
Events:
jamwidgets:comment-posted- Comment submitted (detail contains comment)
Reactions
Reaction buttons (like, love, clap, etc.):
---
import Reactions from "@jamwidgets/astro/Reactions";
---
<Reactions
siteKey={import.meta.env.JAMWIDGETS_SITE_KEY}
pageId={Astro.url.pathname}
reactions={["like", "love", "clap"]}
/>Props:
siteKey(required) - Your Jamwidgets site keypageId(required) - Unique page identifierreactions- Array of reaction types (default:['like'])icons- Custom icons:{ like: '👍', love: '❤️' }endpoint- Base URL (default:https://jamwidgets.com)theme-'light'|'dark'|'auto'(default:'light')class- Additional CSS class
Built-in icons: like, love, clap, fire, think, sad, laugh
Events:
jamwidgets:reaction-added- Reaction addedjamwidgets:reaction-removed- Reaction removed
Subscribe
Email subscription form with double opt-in:
---
import Subscribe from "@jamwidgets/astro/Subscribe";
---
<Subscribe
siteKey={import.meta.env.JAMWIDGETS_SITE_KEY}
buttonText="Subscribe"
placeholder="[email protected]"
/>Props:
siteKey(required) - Your Jamwidgets site keyendpoint- Base URL (default:https://jamwidgets.com)buttonText- Submit button text (default:'Subscribe')placeholder- Email input placeholdersuccessMessage- Custom success messagetheme-'light'|'dark'|'auto'(default:'light')class- Additional CSS class
Events:
jamwidgets:subscribed- Subscription successful
SubscribeForm
A more flexible subscription form that wraps your own markup:
---
import SubscribeForm from "@jamwidgets/astro/SubscribeForm";
---
<SubscribeForm siteKey={import.meta.env.JAMWIDGETS_SITE_KEY}>
<input name="email" type="email" placeholder="Email" required />
<button type="submit">Join newsletter</button>
</SubscribeForm>JavaScript API
For advanced use cases, use the JavaScript API directly:
import {
submitForm,
fetchComments,
postComment,
fetchReactions,
addReaction,
fetchPosts,
fetchPost,
} from "@jamwidgets/astro";
// Submit a form
await submitForm({
siteKey: "your_key",
formSlug: "contact",
data: { name: "John", email: "[email protected]", message: "Hello!" },
});
// Fetch comments
const comments = await fetchComments({
siteKey: "your_key",
pageId: "/blog/my-post",
});
// Add a reaction
await addReaction({
siteKey: "your_key",
pageId: "/blog/my-post",
reactionType: "like",
});Styling
Components inherit their font and text color from the surrounding page by default, while interactive controls use the browser's accent colors. Use a preset when the widget sits outside your site's normal color context:
<Comments theme="auto" ... />Set semantic CSS custom properties on a wrapper or the widget itself to match your site. The same tokens work across every styled component:
.article-widgets {
--jamwidgets-color-text: var(--color-ink);
--jamwidgets-color-muted: var(--color-ink-muted);
--jamwidgets-color-surface: var(--color-paper);
--jamwidgets-color-border: var(--color-rule);
--jamwidgets-color-accent: var(--color-link);
--jamwidgets-color-accent-hover: var(--color-link-hover);
--jamwidgets-color-on-accent: white;
--jamwidgets-focus-ring-color: color-mix(in srgb, var(--color-link) 28%, transparent);
}Status colors can be customized with --jamwidgets-color-success, --jamwidgets-color-error, and --jamwidgets-color-notice. Components adapt to their container width, so subscribe, waitlist, comments, and feedback controls stack when embedded in a narrow column.
License
MIT
