npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pagecraft/sdk

v0.0.30

Published

A powerful SDK for building configurable components for the PageCraft editor.

Readme

@pagecraft/sdk

A powerful SDK for building configurable components for the PageCraft editor.

Table of Contents

Introduction

The PageCraft SDK is a standalone library that enables developers to build and register fully customizable components for PageCraft. These components become available in the drag-and-drop interface, allowing end users to configure and use them seamlessly within PageCraft pages.

What the SDK provides

  • Tools to create reusable and configurable components tailored to your application needs.
  • A command-line interface (CLI) to quickly generate new component templates with predefined structure and configuration support.
  • Support for managing component settings such as width, height, resizability, and more.
  • Utilities, models, and conventions to help keep your components consistent, maintainable, and easily integrable.

With the PageCraft SDK, developers can efficiently extend the capabilities of the page designer with modular components that align with their design and functionality requirements.

Installation

PageCraft SDK is currently part of the PageCraft monorepo and does not need to be installed separately. To get started:

  1. Install all project dependencies from the root of the monorepo:
npm install
  1. Build the SDK package to make it available for use:
npm run build:sdk

After the build is complete, the SDK will be ready for usage within the monorepo. You can then start creating and integrating customizable components for PageCraft.

ℹ️ Note: At this stage, the SDK is only available within the monorepo. In the future, it may be published as a standalone npm package for external usage.

Folder Structure

The components library that uses the SDK is a React package built with Vite. The source files are organized as follows:

libs/components/src/
├── assets `Icons, images, and other static assets`
├── lib
│ ├── components `Customizable components built with the SDK`
│ ├── ui `Reusable UI components used by the customizable components`
│ └── utils `Utility functions and helpers used by the components library`
├── global.css `Global styles applied across the components library`
└── index.ts `Entry point exporting the components library`

This structure helps keep the project organized by separating customizable components, shared UI elements, and utility code, while managing static assets and global styles in dedicated locations.

Getting Started

Follow these steps to generate and register your first customizable component using PageCraft SDK:

Note: These instructions assume you are executing commands from the libs/components directory.

  1. Generate a new component

Navigate to the components folder:

cd src/lib/components

Run the following command, replacing [component-example] with your component name in kebab-case:

npx pagecraft g [component-example]

This will create two files for your new component:

  • component-example.tsx containing the React component
  • component-example.module.css for the component’s scoped styles

Here is an example of what the generated .tsx file looks like:

import * as PageCraft from '@pagecraft/sdk';
import styles from './component-example.module.css';

export default PageCraft.Component({
  label: 'Component Example',
  position: PageCraft.Position.FLEXIBLE,
  defaultWidth: 8,
  defaultHeight: 20,
})(ComponentExample);

function ComponentExample() {
  const [name, setName] = PageCraft.exposeConfigProperty({
    type: PageCraft.Types.Text,
    name: 'name',
    initialValue: 'Component Example',
  });

  return (
    <div className={styles.container}>
      <h2>
        🤖 Component <strong>{name}</strong> created. PageCraft saves your dev-sanity again.
      </h2>
    </div>
  );
}
  1. Register the Component

Open src/index.ts and register your new component:

import './global.css';

import { ComponentLibrary } from '@pagecraft/sdk';

import ComponentExample from '@/lib/components/component-example';

export const library: ComponentLibrary = {
  name: 'Component Library',
  components: {
    ComponentExample,
  },
};
  1. Build the components library

After making changes, rebuild the component library:

cd [project-root]
npm run build:components

Your component is now ready to be used within PageCraft.

You can also start the components library in watch mode by running npm run dev:components from the root of the project.

Component Settings

To make a component compatible with PageCraft and define how it behaves in the page designer, it must be wrapped using the PageCraft.Component decorator. This decorator allows you to configure various metadata and layout-related settings.

Base Component Settings

All components support the following base settings:

export interface ComponentSettingsBase {
  label?: string; // Label shown in the PageCraft UI
  imageUrl?: string; // Thumbnail used for the component card, should be placed in the assets folder
  singleton?: boolean; // If true, the component can be added only once per page
  category?: ComponentCategory; // The category this component belongs to
}

export enum ComponentCategory {
  UIElements = 'UI Elements',
  Layout = 'Layout',
  Forms = 'Forms',
  Data = 'Data',
  Navigation = 'Navigation',
  Other = 'Other',
}

These settings define how your component appears and behaves in the designer's UI.

Positioning Options

Components can be positioned in one of two ways: flexible or fixed.

1. Flexible Position

Flexible components can be dragged and dropped anywhere, resized, and moved freely. Use the following interface for flexible settings:

export interface FlexibleComponentSettings extends ComponentSettingsBase {
  position: Position.FLEXIBLE;
  defaultWidth?: number; // Default width in grid columns (max 24)
  defaultHeight?: number; // Default height in 8px rows
  resizable?: {
    horizontal?: boolean;
    vertical?: boolean;
  }; // Controls whether the component can be resized
}

🧠 The layout system is based on a 24-column grid with 8px tall rows.

Example:

import * as PageCraft from '@pagecraft/sdk';

PageCraft.Component({
  position: Position.FLEXIBLE,
  label: 'My Flexible Component',
  imageUrl: 'flexible-thumbnail.png',
  defaultWidth: 8, // 8 columns
  defaultHeight: 10, // 10 rows → 80px height
  resizable: {
    horizontal: true,
    vertical: false,
  },
})(MyComponent);

2. Fixed Position

Fixed components are positioned absolutely relative to the viewport and do not follow the grid layout for flexible content. Instead, they can be precisely positioned using grid coordinates or centered on the screen.

export interface FixedComponentSettings extends ComponentSettingsBase {
  position: Position.FIXED;
  left?: number; // Distance from the left edge, in grid columns (max 24)
  right?: number; // Distance from the right edge, in grid columns
  top?: number; // Distance from the top, in rows (8px per row)
  bottom?: number; // Distance from the bottom, in rows (8px per row)
  width?: number; // Width in grid columns
  height?: number; // Height in 8px rows
  centerHorizontally?: boolean; // If true, horizontally centers the component in the viewport. Should be used with `width`
  centerVertically?: boolean; // If true, vertically centers the component. Should be used with `height`
}

🧠 left, right, top, and bottom values follow the same grid system: 24 columns wide and 8px per row vertically.

📐 Use centerHorizontally and centerVertically along with width or height respectively to align the component in the center of the screen.

This is ideal for overlay components or elements that must remain in a fixed spot on the screen (like modals or floating buttons).

Example:

import * as PageCraft from '@pagecraft/sdk';

PageCraft.Component({
  position: Position.FIXED,
  label: 'My Fixed Component',
  imageUrl: 'fixed-thumbnail.png',
  top: 2, // 2 rows → 16px from top
  left: 6, // 6 columns from left
  width: 6, // 6 columns wide
  height: 8, // 8 rows → 64px tall
})(MyFixedComponent);

Or to center the component:

import * as PageCraft from '@pagecraft/sdk';

PageCraft.Component({
  position: Position.FIXED,
  label: 'Centered Fixed Component (e.g. a popup modal)',
  imageUrl: 'centered-thumbnail.png',
  width: 10,
  height: 12,
  centerHorizontally: true,
  centerVertically: true,
})(MyCenteredComponent);

Configuration Properties

Configuration properties enable components to expose editable fields that users can customize directly from PageCraft’s Inspector panel. These properties can represent anything from text and numbers to booleans, dropdown selections, sliders, and more.

To define and manage individual configuration properties, use the PageCraft.exposeConfigProperty hook:

import * as PageCraft from '@pagecraft/sdk';

const value = PageCraft.exposeConfigProperty({
  type: PageCraft.Types.Text,
  name: 'propName',
  label: 'Property Label',
  initialValue: 'Default Value',
});

What it does:

  1. Registers the property with PageCraft, making it editable in the Inspector panel.
  2. Returns the current value of the property for use in your component.
  3. Automatically updates when the user modifies the property in the Inspector panel.

⚠️ Each config property must have a unique name within the same component.

You can use multiple calls to exposeConfigProperty within a single component to register and access multiple configurable properties.

Base Property Settings

All configuration properties share a common base interface:

interface ComponentPropertyBase {
  name: string; // Unique key for the property (must be unique within the component)
  label?: string; // Optional label shown in the Inspector
  description?: string; // Optional help text shown in the Inspector
}

Each property type extends this interface with additional settings relevant to its behavior and presentation.

Supported Property Types

PageCraft supports the following property types for configuration:

| Type | Description | | -------------- | ------------------------------------- | | Text | Single-line text input | | TextArea | Multi-line text input | | Icon | Icon picker, integrates with iconify | | Color | Color picker | | RichText | Rich text editor | | Number | Numeric input | | Slider | Slider input for number ranges | | Checkbox | Checkbox toggle (true/false) | | Switch | Switch toggle (true/false) | | Tabs | Tab-based option selector | | Select | Dropdown with predefined options | | Collection | Repeatable list of grouped properties |

Text Properties

Interface

export interface ComponentStringProperty extends ComponentPropertyBase {
  type: types.Text | types.RichText;
  initialValue?: string;
  placeholder?: string;
}

Example

{
  type: PageCraft.Types.Text, // or PageCraft.Types.RichText
  name: 'propName',
  label: 'User-friendly Label',
  description: 'Help text for the property',
  initialValue: 'Default text value',
  placeholder: 'Optional placeholder text'
}

Number Properties

Interface

export interface ComponentNumberProperty extends ComponentPropertyBase {
  type: types.Number | types.Slider;
  initialValue?: number;
  min?: number;
  max?: number;
  placeholder?: string;
}

Example

{
  type: PageCraft.Types.Number, // or PageCraft.Types.Slider
  name: 'propName',
  label: 'User-friendly Label',
  description: 'Help text for the property',
  initialValue: 5,
  min: 0, // Optional minimum value
  max: 10, // Optional maximum value
  placeholder: 'Optional placeholder text'
}

Boolean Properties

Interface

export interface ComponentBooleanProperty extends ComponentPropertyBase {
  type: types.Checkbox | types.Switch;
  initialValue?: boolean;
}

Example

{
  type: PageCraft.Types.Checkbox, // or PageCraft.Types.Switch
  name: 'propName',
  label: 'User-friendly Label',
  description: 'Help text for the property',
  initialValue: false
}

Enum Properties

Interface

export interface ComponentEnumProperty<T = string | number | boolean | null> extends ComponentPropertyBase {
  type: types.Tabs | types.Select;
  options: Array<{ value: T; label: string }>;
  initialValue?: T;
  placeholder?: string;
}

Example

{
  type: PageCraft.Types.Tabs, // or PageCraft.Types.Select
  name: 'propName',
  label: 'User-friendly Label',
  description: 'Help text for the property',
  options: [
    { value: 'option1', label: 'Option 1' },
    { value: 'option2', label: 'Option 2' }
  ],
  initialValue: 'option1',
  placeholder: 'Optional placeholder text'
}

Icon Properties

Interface

export interface ComponentIconProperty extends ComponentPropertyBase {
  type: types.Icon;
  initialValue?: string;
}

Example

import * as PageCraft from '@pagecraft/sdk';

const icon = PageCraft.exposeConfigProperty({
  type: PageCraft.Types.Icon,
  name: 'icon',
  label: 'Icon',
  initialValue: 'mdi:home'
});

...

return (
  <PageCraft.Icon icon={icon} width={24} height={24} noobserver />
)

Collection Properties

Collections allow you to create complex nested data structures:

Interface

export interface ComponentCollectionProperty extends ComponentPropertyBase {
  type: types.Collection;
  itemSchema: ComponentCollectionItemSchema;
  initialValue?: Array<ComponentCollectionItemValue>;
}

export interface ComponentCollectionItemSchema {
  props: ReadonlyArray<PrimitiveComponentProperty>;
  viewPropName?: string;
  childItemSchema?: ComponentCollectionItemSchema;
}

export interface ComponentCollectionItemValue {
  value: Record<string, PrimitiveComponentProperty['initialValue']>;
  children?: ComponentCollectionItemValue[];
}

export type PrimitiveComponentProperty =
  | ComponentStringProperty
  | ComponentNumberProperty
  | ComponentBooleanProperty
  | ComponentEnumProperty;

Example

{
  type: PageCraft.Types.Collection,
  name: 'items',
  label: 'Collection Items',
  itemSchema: {
    props: [
      {
        type: PageCraft.Types.Text,
        name: 'title',
        label: 'Title'
      },
      {
        type: PageCraft.Types.Text,
        name: 'description',
        label: 'Description'
      }
    ],
    viewPropName: 'title', // Property to display in the collection editor
    childItemSchema: {
      // Optional nested collection items
      props: [
        {
          type: PageCraft.Types.Text,
          name: 'subItem',
          label: 'Sub Item'
        }
      ]
    }
  },
  initialValue: [
    { value: { title: 'Item 1', description: 'Description 1' } },
    { value: { title: 'Item 2', description: 'Description 2' } }
  ]
}

Using collections with TypeScript:

const items =
  PageCraft.exposeConfigProperty <
  PageCraft.CollectionList<
    {
      title: string;
      description: string;
    },
    PageCraft.CollectionList<{
      subItem: string;
    }>
  >({
    type: PageCraft.Types.Collection,
    // other properties...
  });

Creating Portals

In some cases, you may need to render a component outside of the component grid—for example, when building modals, tooltips, or overlays. PageCraft provides a utility method for this: PageCraft.createPortal.

Under the hood, this function wraps ReactDOM.createPortal, and injects your component into a dedicated portal container within the PageCraft editor.

You can use the createPortal method like so:

return PageCraft.createPortal(<div>Your modal or overlay content</div>);

This will inject your content into the PageCraft portal container, preserving layering and positioning context.

Adding Fonts

If your components use external fonts, you need to register those font URLs in the component library declaration to ensure they are loaded properly inside the PageCraft editor and runtime environment.

Fonts are declared via the fontUrls field in the library configuration object defined in src/index.ts:

export const library: ComponentLibrary = {
  name: 'Component Library',
  fontUrls: [
    'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap',
    'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap',
    ...
  ],
  components: {
    ComponentExample,
    ...
  },
};

The fontUrls array should include fully qualified URLs pointing to your font stylesheets (e.g. from Google Fonts).

Best Practices and Restrictions

  • Use only functional components defined with named function declarations; avoid class components and arrow functions.
  • Do not reuse existing configuration property names within the same component to prevent naming conflicts.
  • Do not use @import statements to load fonts. Instead, register fonts as shown in the "Adding Fonts" section.
  • Avoid using fixed positioning for elements. Use PageCraft.createPortal to inject overlays or modals instead.
  • Avoid installing large third-party dependencies that significantly increase the bundle size.

TypeScript Types

The SDK provides comprehensive TypeScript type definitions for all APIs:

import {
  // Component property types
  types,
  ComponentProperty,
  ComponentStringProperty,
  ComponentNumberProperty,
  ComponentBooleanProperty,
  ComponentEnumProperty,
  ComponentCollectionProperty,
  ComponentCollectionItemSchema,
  ComponentCollectionItemValue,
  PrimitiveComponentProperty,
  CompositeComponentProperty,

  // Component settings types
  Position,
  ComponentCategory,
  FlexibleComponentSettings,
  FixedComponentSettings,
  ComponentSettings,

  // Collection helper types
  CollectionList,
} from '@pagecraft/sdk';