Creating a New Element
ShipEngine Elements is a collection of React components that provide shipping functionality through a standardized interface.
This guide provides the essential steps for creating new elements in the ShipEngine Elements library.
Element Architecture
Each element is composed of:
- Component: Your main React component logic
- createElement Wrapper: Provides error boundaries, i18n, and styling
- Element Provider: Manages global state and configuration
File Structure
libs/elements/src/elements/new-element/
├── index.ts # Export file
├── new-element.tsx # Main element file
├── __stories__/ # Storybook stories
│ └── new-element.stories.tsx
└── __tests__/ # Jest tests
└── new-element.test.tsx
Step-by-Step Guide
1. Create the Directory Structure
mkdir -p libs/elements/src/elements/new-element/{__stories__,__tests__}
2. Create the Main Element File
libs/elements/src/elements/new-element/new-element.tsx
import { ErrorFallback } from "@components/error-fallback";
import { Button, Typography } from "@shipengine/giger";
import { useTranslation } from "react-i18next";
import { createElement } from "../../create-element";
import { en } from "../../locales";
export type NewElementProps = {
title?: string;
onClick?: (value: any) => void;
};
export const Component = ({ title = "New Element Title", onClick }: NewElementProps) => {
const { t } = useTranslation();
const handleOnClick = () => {
const value = { name: "ShipEngine Elements" };
onClick?.(value);
};
return (
<div>
<Typography variant="h2">{title}</Typography>
<Typography variant="body1">{t("description")}</Typography>
<Button onClick={handleOnClick}>{t("actionButton")}</Button>
</div>
);
};
export const Element = createElement(Component, ErrorFallback, {
css: {
height: "100%",
maxWidth: "800px",
minWidth: "440px",
width: "100%",
},
resources: { en },
});
export type ElementProps = React.ComponentProps<typeof Element>;
3. Create the Index File
libs/elements/src/elements/new-element/index.ts
export * as NewElement from "./new-element";
export type { NewElementProps } from "./new-element";
4. Add Localization Resources
libs/elements/src/locales/en/new-element.json
{
"description": "This is a new Element!",
"actionButton": "Continue"
}
5. Create Storybook Stories
libs/elements/src/elements/new-element/__stories__/new-element.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import { NewElement } from "../new-element";
const meta: Meta<typeof NewElement.Element> = {
title: "Elements/NewElement",
component: NewElement.Element,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
title: "Default Example",
},
};
export const CustomTitle: Story = {
args: {
title: "Custom Element Title",
},
};
7. Update Package Exports
Add your new element to libs/elements/package.json exports:
{
"exports": {
"./new-element": {
"source": "./src/elements/new-element/index.ts",
"import": {
"types": "./dist/types/elements/new-element/index.d.ts",
"default": "./dist/esm/elements/new-element/index.js"
},
"require": {
"types": "./dist/types/elements/new-element/index.d.ts",
"default": "./dist/cjs/elements/new-element/index.cjs"
}
}
}
}
8. Update Main Index File
Add your element to libs/elements/src/index.ts:
export * from "./elements/new-element";
Best Practices
- Use TypeScript with proper type definitions
- Include JSDoc comments where appropriate
- Write unit tests for functionality
- Use i18 translation strings for all user-facing text
- Follow existing code patterns for consistency
- Test with Storybook during development