
Security News
The Changelog Podcast: Practical Steps to Stay Safe on npm
Learn the essential steps every developer should take to stay secure on npm and reduce exposure to supply chain attacks.
@best-apps/gfx-editor
Advanced tools
Use Best Apps' GFX Editor as a React component or in vanilla JS
npm install @best-apps/gfx-editor
yarn add @best-apps/gfx-editor
import {GFXInstanceProvider, GFXInstance} from '@best-apps/gfx-editor';
function App() {
return (
<GFXInstanceProvider>
<div
style={{
width: 400,
height: 600,
margin: 20,
border: '1px solid black',
position: 'relative', // You have to wrap the editor in an element with width/height and position: fixed/relative/absolute
}}
>
<GFXInstance
v1TemplateId={123} // The v1TemplateId for you product and design
/>
</div>
</GFXInstanceProvider>
);
}
That will render the editor in an iframe and give you the full interface for interacting with your design/template/product
Wherever you want to put the Editor you have to put it inside <GFXInstanceProvider />. This creates a React context so
that every component nested within it has access to gfx with useGFXInstance().
Use <GFXInstance {...props} /> wherever you want to render the Editor in an iframe.
div with width, height, and position styles so that the editor can fill this container element.gfx within a GFXInstanceProvider component.gfxRef. It will get initialized with a gfx instance object.This hook will get gfx from context. Here is an example:
import {useGFXInstance} from '@best-app/gfx-editor';
function Button() {
const gfx = useGFXInstance();
return <button onClick={() => gfx?.actions.flipCanvas()}>Flip canvas</button>;
}
If you are attempting to get the parent gfx from context in a React class component, use GFXInstanceContext directly. Here is an example:
import {GFXInstanceContext} from '@best-app/gfx-editor';
class ButtonWrapper extends React.Component {
static contextType = GFXInstanceContext;
render() {
const gfx = this.context;
return (
<button onClick={() => gfx?.actions.flipCanvas()}>Flip canvas</button>
);
}
}
You can also use GFXInstanceConsumer with a child render callback to access the parent gfx from context. Here is an example:
import {GFXInstanceConsumer} from '@best-app/gfx-editor';
function Button() {
return (
<GFXInstanceConsumer>
{(gfx) => {
<button onClick={() => gfx?.actions.flipCanvas()}>Flip canvas</button>;
}}
</GFXInstanceConsumer>
);
}
NOTE: This is only useful if you are creating your own UI for the Editor.
When creating your own toolbars and buttons, you will often need to know exactly what TYPE OF object has been
actively selected. This is where useActiveObjectType is useful.
This hook will return:
CustomImageLikeCustomTextboxStickerCustomizableTextSlotCustomizableImageSlotnullHere is an example:
function TopToolbar() {
const activeObjectType = useActiveObjectType(gfx?.state);
if (activeObjectType === 'LikeCustomTextbox') {
return (
<button
onClick={() =>
gfx?.actions.openMenu({
menuType: 'strokeColorOnTextbox',
})
}
>
Open stroke color drawer
</button>
);
}
}
Sort of like ReactDOM.render, you call this with a config object and an html element where we should insert the
Editor.
import {embedGFX} from '@best-apps/gfx-editor';
const gfx = embedGFX(
{
v1TemplateId: 1234,
},
document.getElementById(#editor - container),
);
Once you have the gfx object, you can build a UI that incorporates gfx.actions and gfx.state
You call these like gfx.actions.flipCanvas()
flipCanvas(): This flips the canvas (if there is a front and back to the design and product). Wall art, for example,
will not flip.rotateCanvas(): This will rotate the canvas (only when using wall art or poster printables)openMenu: (activeMenu: ActiveMenuType) => Promise<void>. This opens a menu/drawer. ActiveMenuType is one of these:
openMenu({ menuType: 'sticker' }): to open the sticker draweropenMenu({ menuType: 'printableColor' }): to open the color selection drawer to change the color on the garment or wall art (what we call "printables")openMenu({ menuType: 'strokeColorOnTextbox' }): to open the color selection drawer to select the color for the stroke on text in a textboxopenMenu({ menuType: 'fillColorOnTextbox' }): to open the color selection drawer to select fill color on text in a textboxopenMenu({ menuType: 'fontFamilyOnTextbox' }): to open the sticker draweropenMenu({ menuType: 'fillColorOnAllTextboxes' }): to open the color selection drawer to select fill color on ALL text in ALL textboxes and text slotscloseMenu: () => void: Closes the menu (any menu)setFillColorOnTextbox: (color: string) => void: This will set the fill color on text in a textboxsetFillColorOnTextboxSlot: (slotId: number, color: string) => void: Set fill color on text in a text slotsetFillColorOnAllTextboxes: (color: string) => void: Sets the fill color on ALL text boxessetFontFamilyOnTextbox: (...args: any) => void: Sets the font family on a textboxsetStrokeColorOnTextbox: (...args: any) => Promise<void>: Sets the stroke color on a given textboxsetImageOnImageSlot: (...args: any) => Promise<void>: Sets the image on an image slotsetPrintableColor: (color: string) => void: Sets the printable color to the specified colornextColor: () => void: Toggles the color on the "printable"updateDesignOnState: () => void: This just creates an export of the design and put is on gfx.state.designdebouncedUpdateDesignOnState: () => Promise<void>: A debounced version of the updateDesignOnState methodtoggleZoom: () => void: Toggles the zoom on the editor canvasaddText: () => Promise<void>: Adds a customizable textbox to the canvasaddImage: (urlOrBase64: string, addImageOptions?: AddImageOptions) => Promise<void>: Adds an image to the canvasrotateActiveObject: (angle: number, animate?: boolean) => void: Rotates the active objectalignActiveObject: (position: AlignPosition, animate?: boolean) => void: Aligns the active object depending on the arguments you pass in:
AlignPosition: 'center' | 'centerHorizontally' | 'centerVertically' | 'alignToTop'showAlert: (alertOptions: AlertOptions) => void: Shows an alert you specify with alertOptions
title: string: The title of the alertbody?: string: The body of the alerttimeout?: number: How long before the alert is dismissed automaticallydismissable: boolean: Whether or not the alert can be dismisseddismissableButtonLabel?: string: And what the dismiss button label ishideAlert: () => void: Hides any active alertgetProofs: (quality?: number) => Promise<GFXProofs>: Gets proofs and sends back their base64 representationsaveDesign: (options?) => Promise<void>: Saves the design to our db
'low' | 'medium' | 'high'removeSlot: (props: { uuid: string }) => Promise<void>: Removes a slot by its UUID.setFontSizeOnTextbox: (fontSize: number) => void: Sets the font size on the active textbox.setInchesSize: (width: number, height: number) => Promise<void>: Sets the size of the active object in inches.getInchesSize: (uuid?: string) => { width: number, height: number } | null: Gets the size of the specified object in inches.setCustomPropertyOnSlot: (slotIdentifier: string | number, value: string, textValue?: string) => Promise<void>: Sets a custom property on a slot such as new image or other value.applySlotProperties: (objects: GFXCanvasObjectType[]) => Promise<void>: Applies properties to slots based on the object list passed.setSlotAlias: (props: { alias: string, uuid: string }) => Promise<void>: Sets an alias for a slot.setFitToContainer: (fitToContainer: boolean) => Promise<void>: Sets whether the slot should fit to the container. fitToContainer in false should crop to match the container (Only work for images slots).moveElementTo: (uuid: string, newPosition: number) => Promise<void>: Moves an element to a new position. Updates Z Index position.runWalkthroughAction: (step: number) => Promise<void>: Runs a walkthrough action.highlightSlotById: (slotId: number, selectSlot?: boolean, toggleSlot?: boolean) => Promise<void>: Highlights a slot by its ID.setActiveObjectNegative: (negative: boolean) => Promise<void>: Sets the active object as negative.setTextAlign: (align: 'left' | 'center' | 'right') => Promise<void>: Sets the text alignment on the active textbox.uploadImage: () => Promise<void>: Uploads an image.getSides: () => Promise<number>: Gets the number of sides in the design.getTemplateInfo: () => Promise<EditedTemplateInfo>: Gets the template information.adjustSlotsScale: (scale: number, topSpace: number, section: SectionType) => Promise<void>: Adjusts the scale of slots.setObjectActive: (uuid: string) => void: Sets an object as active by its UUID.copy: () => void: Copies the active object.paste: () => string | null: Pastes the copied object in the current canvas.duplicate: () => string | null: Duplicates the active object.removeObject: () => Promise<void>: Removes the active object.setScaleCenter: (scale: number) => Promise<void>: Sets the scale of the active object to the center.toggleGroupAllObjects: () => Promise<void>: Toggles grouping of all objects.setCustomTextWidth: (width: number) => Promise<void>: Sets the custom width of the active text object.fitGroupToPrintableArea: () => Promise<void>: If toggle group enable, it fits the group to the printable area.validateIsEditorReady: () => Promise<boolean>: Validates if the editor is ready by checking if all sections, images, and fonts are loaded. It will return true after all are loaded.setFontStyle: (fontStyle?: 'normal' | 'italic' | 'oblique') => Promise<void>: Sets the font style (normal, italic, or oblique) on the active textbox. If called with no argument, resets the font style to 'normal'.setShadow: (options?: { color: string, blur: number, offsetX: number, offsetY: number } | string) => Promise<void>: Sets the shadow of the active textbox. Pass an object with color, blur, offsetX, and offsetY, or a string for shorthand. If called with no argument, removes the shadow from the active textbox.setStroke: (options?: { color: string, width: number }) => Promise<void>: Sets the stroke color and width of the active textbox. If called with no argument, removes the stroke from the active textbox.These are properties on gfx state. You might access them like gfx.state.isZooming
isSyncing: booleancornerIcons: CornerIconscanFlip: booleancanRotate: booleancanChangeColor: booleanstatus: StatusstatusCode: StatusCodeorientation: PrintableInfoOrientationactiveObject: GFXCanvasObjectType | nullisZooming: booleanscale: numbercenterPoints: GFXCenterPointsBySectionalert?: AlertOptions | nullactiveMenudesign: V2DesignactiveSection: SectionTypeactiveV2Printable: V2PrintableisSupportedBrowser: booleanproductId: number | string | nulldesignId: number | string | nulldesignNumber: string | nullv1TemplateId: number | string | nullinitialData: GFXInitialDataTypewindowWidth: number | nullwindowHeight: number | nullstickers: number[]You call these like gfx.actions.flipCanvas()
onStateChange: (payload: GFXStateType) => void: your favorite probably, a way to listen for state changesonColorChanged: (color: string) => void: when the printable/garment color is changedonTextFillColorChanged: (color: string) => void: when text fill color is changedonObjectRemoved: (obj: GFXCanvasObjectType) => void:onObjectDoubleClicked: (obj: GFXCanvasObjectType) => void:onSelectionCleared: (payload: {:deselected: GFXCanvasObjectType[] | null:selected: GFXCanvasObjectType[] | null:}) => void:onImageSelectedInSlot: (obj: GFXCanvasObjectType) => void:onBeforeSelectionCleared: (payload: { deselected: GFXCanvasObjectType[] }) => void:onObjectSelectionUpdated: (payload: {:onImageIdUpdatedOnSlot: (payload: { slotId: number; imageId: number }) => void:onUpdatedCustomSlot: (payload: any) => void:Example usage:
var gfx = window.embedGFX(
{
v1TemplateId: 829, // Just an example v1TemplateId
interfaceType: 'full',
},
document.getElementById('gfx-product'),
);
gfx.addEventListener('onStateChange', (state) => console.log(state));
We use BEM style classnames throughout the editor UI, and you can provide a stylehseet as a text string in the gfxConfig.customOptions.css property in order to override our existing styles.
var gfx = window.embedGFX(
{
v1TemplateId: 829, // Just an example v1TemplateId
interfaceType: 'full',
gfxConfig: {
disableTOS: true, // to hide the watermark (only applicable on full interface)
showWatermark: false, // to hide the watermark (only applicable on full interface)
showInitialToast: true, // show the initial toast message by default
customOptions: {
css: `
.AbstractDrawer {
background: #fff !important;
border-top: 1px solid #2b2c2d18;
border-radius: 0;
}
`,
},
},
},
document.getElementById('gfx-product'),
);
We provided a usefull Shopify App to embed our GFX Editor into your shopify Theme. To install the app into your store, check this documentation: Shopify App
When integrating your Shopify store with gfx you must
design_numberapp_flow: 'embedded'To get the design_number, you must call gfx.actions.saveDesign() which will return a SavedDesign object
This repo contains a working example of how to integrate gfx with Shopify using liquid.
Here is an example of how you would do this with an ajax request with Jquery:
const savedDesign = await gfx.actions.saveDesign();
await $.post(
'/cart/add.js',
{
quantity: parseInt(quantity.value),
id: parseInt(window.currentVariant),
attributes: {
app_flow: 'embedded',
},
properties: {
design_number: savedDesign.designNumber,
},
},
null,
'json',
);
This usually requires custom implementation support right now. Please contact your GFX account manager.
Basically, you must add the properties to your form:
<form method="post" action="/cart/add">
<input type="hidden" name="properties[design_number]" value="DESIGN_NUMBER" />
<input type="text" name="id" value="VARIANT_ID" />
<input type="submit" value="submit" />
</form>
Our designs seamlessly integrate with your workflow. Once you’ve crafted your design in the gfx editor, simply utilize our API to send it directly. The API accepts data structured in a specific format, ensuring smooth integration with your existing systems. Check docs here
We have a testing framework that allows you to run end to end tests on your designs. This is useful for testing your designs in a CI/CD pipeline. To properly run the test before going to production you must have ACT installed and run:
act -j scheduled-e2e-local --container-architecture linux/amd64 --reuse \
--secret ADMIN_API_V2_URL_STG="https://stg.api.gfxco.com" \
--secret ADMIN_API_V2_URL_PRD="https://api.gfxco.com"
More information about act: https://nektosact.com/usage/runners.html
FAQs
GFX editor
We found that @best-apps/gfx-editor demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 5 open source maintainers 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
Learn the essential steps every developer should take to stay secure on npm and reduce exposure to supply chain attacks.

Security News
Experts push back on new claims about AI-driven ransomware, warning that hype and sponsored research are distorting how the threat is understood.

Security News
Ruby's creator Matz assumes control of RubyGems and Bundler repositories while former maintainers agree to step back and transfer all rights to end the dispute.