Draw arrows between components in React!
- Connect between elements by passing a ref or an id for startElement and endElement.
- Automatic anchoring based on smallest path.
- Fast algorithm to find path and to adjust canvas.
- Customization is easy but flexible
found a problem? not a problem! post a new issue(here).
liked my work? please star this repo.
with npm npm install react-xarrows
.
(or yarn add react-xarrows
)
see here! codebox of few examples(in this repo at /examples).
see this interactive example: https://lwwwp.csb.app/CustomizeArrow
import React, {useRef} from "react";
import Xarrow from "react-xarrows";
const boxStyle = {
border: "grey solid 2px",
borderRadius: "10px",
padding: "5px",
};
function SimpleExample() {
const box1Ref = useRef(null);
return (
<div style={{display: "flex", justifyContent: "space-evenly", width: "100%"}}>
<div ref={box1Ref} style={boxStyle}>
hey
</div>
<p id="elem2" style={boxStyle}>
hey2
</p>
<Xarrow
start={box1Ref} //can be react ref
end="elem2" //or an id
/>
</div>
);
}
export default SimpleExample;
react-xarrows does not renders automatically if one of the connected elements is rendered. You have to manually trigger an update on the arrows whenever one of the connected elements renders(possibaly by trigger update on the parent of the arrows) ,this is because the Xarrow component does not have any control or awareness of the connected elements. in addition, make sure to render Xarrows later in the DOM then the connected elements else the app will crash. this is planned to be changed in react-xarrows v2.
Want a feature that is not supported? found a bug?
no need to clone the repo and set up the dev environment anymore!
here's a ready to use development environment with a click of a button(patience, it takes about a minute to setup):
this will set up environment that will clone react-xarrow master,and will link the code from the src to the examples,
and will start examples,with typescript watch process that will recompile when any change is made.
this means that any code changes in src/index.tsx will immediately be reflected to the running example at port 3000!
(add console.log("test") line and see!)
to reproduce this dev env on your local machine git clone and follow same commands as in gitpod.yml.
if you made an improvement that is relevant for most users, you can quickly submit a pull request.
Please always pull request from and into dev branch -
here's Gitpod
v2 is on its way. want to contribute and participate in plannig the next react architecture for react-xarrows? see discussion here!
to see full typescript definition see types.ts file.
here's a summary of the all the available props:
Properties | Description | default value | type |
---|---|---|---|
start | ref to start element | none(Required!) | string/ReactRef |
end | ref to end element | none(Required!) | string/ReactRef |
startAnchor | from which side the arrow should start from start element | 'auto' | string/object/array |
endAnchor | at which side the arrow should end at end element | 'auto' | string/object/array |
label | optional labels | null | string/array |
color | color of Xarrow(all parts) | 'CornflowerBlue' | string |
lineColor | color of the line | null | string |
headColor | color of the head | null | string |
tailColor | color of the tail | null | string |
strokeWidth | thickness of Xarrow(all parts) | 4 | number |
headSize | thickness of head(relative to strokeWidth) | 6 | number |
tailSize | thickness of tail(relative to strokeWidth) | 6 | number |
path | path drawing style | 'smooth' | string |
curveness | how much the line curveness when path='smooth' | 0.8 | number |
gridBreak | where the line breaks in path='grid' | 0.5 | number |
dashness | should the line be dashed | false | boolean/object |
showHead | show the arrow head? | true | boolean |
showTail | show the arrow tail? | false | boolean |
showXarrow | show Xarrow? | true | boolean |
animateDrawing | animate drawing when arrow mounts? | false | boolean/object |
headShape | shape of the arrow head | 'arrow1' | string/object |
tailShape | shape of the arrow tail | 'arrow1' | string/object |
Advanced Props
Properties | Description | default value | type |
---|---|---|---|
passProps | properties which will be pased to arrowBody,arrowHead,arrowTail | {} | object |
SVGcanvasProps | properties which will be passed to svgCanvas | {} | object |
arrowBodyProps | properties which will be passed to arrowBody | {} | object |
arrowHeadProps | properties which will be passed to arrowHead | {} | object |
arrowTailProps | properties which will be passed to arrowTail | {} | object |
divContainerProps | properties which will be passed to divContainer | {} | object |
SVGcanvasStyle | style properties which will be passed svgCanvas | 0 | object |
divContainerStyle | style properties which will be passed divContainer | false | object |
_extendSVGcanvas | extend svgCanas at all sides | 0 | number |
_debug | show debug elements | 0 | boolean |
_cpx1Offset | offset control point 1 x | 0 | number |
_cpy1Offset | offset control point 1 y | 0 | number |
_cpx2Offset | offset control point 2 x | 0 | number |
_cpy2Offset | offset control point 2 x | 0 | number |
This API is built in such way that most props can accept different types. you can keep things simple or provide more
custom props for more custom behavior - the API except both(see startAnchor
or label
properties for good examples)
.
see typescript types above for detailed descriptions of what type excepts every prop.
This documentation is examples driven.
The examples is sorted from the most common use case to the most custom one.
'start' and 'end'
required
can be a reference to a react ref to html element or string - an id of a DOM element.
examples:
start="myid"
-myid
is id of a dom element.start={myRef}
-myRef
is a react ref.
'startAnchor' and 'endAnchor'
specify what anchors are allowed. can be a string/object/array.
type:
export type anchorType = anchorPositionType | anchorCustomPositionType;
type:
export const tAnchorEdge = ['middle', 'left', 'right', 'top', 'bottom', 'auto'] as const;
export type anchorPositionType = typeof tAnchorEdge[number];
one of "auto" | "middle" | "left" | "right" | "top" | "bottom"
auto
will choose automatically the path with the smallest length.
example:
endAnchor="middle"
will set the anchor of the end of the line to the middle of the end element.
type:
export type anchorCustomPositionType = {
position: anchorPositionType;
offset: { rightness?: number; bottomness?: number };
};
you can offset the anchor from normal positioning. NOTE: breaking changes in naming in v2.
example:
endAnchor= { position: "auto", offset: { rightness: 20 } }
will choose automatic anchoring for end anchor but will offset it 20 pixels to the right after normal positioning.
if list is provided - the minimal length anchors will be chosen from the list. example:
endAnchor= ["right", {position: "left", offset: {bottomness: -10}}]
only right and left anchors will be allowed for endAnchor, and if the left side connected then it will be offset 10 pixels up.
label
you can place up to 3 labels. see examples
label="middleLabel"
- middle labellabel=<div style={{ fontSize: "1.3em", fontFamily: "fantasy", fontStyle: "italic" }}>styled middle label</div>
- custom middle labellabel={{ start:"I'm start label",middle: "middleLabel",end:<div style={{ fontSize: "1.3em", fontFamily: "fantasy", fontStyle: "italic" }}>big end label</div> }}
start and middle label and custom end label
color,lineColor and headColor and tailColor
color
defines color to the entire arrow. lineColor,headColor and tailColor will override color specifically for
line,tail or head. examples:
color="red"
will change the color of the arrow to red(body and head).headColor="red"
will change only the color of the head to red.tailColor="red"
will change only the color of the tail to red.lineColor="red"
will change only the color of the body to red.
strokeWidth and headSize and tailSize
strokeWidth defines the thickness of the entire arrow. headSize and tailSize defines how big will be the head or tail relative to the strokeWidth. examples:
strokeWidth={15}
will make the arrow more thick(body and head).headSize={15}
will make the head of the arrow more thick(relative to strokeWidth as well).tailSize={15}
will make arrow's tail thicker.
path
path
can be one of: "smooth" | "grid" | "straight"
, and it controls the path arrow is drawn, exactly how their name
suggest. examples:
path={"grid"}
will draw the line in sharp curves(90 degrees) like grid.
curveness
defines how much the lines curve. makes a difference only in path='smooth'
. examples:
curveness={false}
will make the line straight without curves(exactly like path='straight').curveness={true}
will choose default values of curveness.curveness={2}
will make Xarrow extra curved.
gridBreak
defines where the line will break when path='grid'
. value should be a number from 0 to 1.
examples:
gridBreak={0.2}
the line would break closer to start element(20% of the path instead of 50%).
dashness
can make the arrow dashed and can even animate. if true default values(for dashness) are chosen. if object is passed then default values are chosen except what passed. examples:
dashness={true}
will make the line of the arrow to be dashed.dashness={{ strokeLen: 10, nonStrokeLen: 15, animation: -2 }}
will make a custom looking dashness.
showHead, showTail and showXarrow
showXarrow
: show or not show Xarrow? (can be used to restart the drawing animation)
showHead
: show or not the arrow head?
showTail
: show or not the arrow tail?
showXarrow={false}
- will hide (unmount) Xarrow and his labels.showHead={false}
- will hide the arrow head.
animateDrawing
can animate the drawing of the arrow using svg animation. type: boolean|number. if true animation duration is 1s. if number is passed then animation duration is number's value in seconds. examples:
animateDrawing
will animate the drawing of the arrow in 1 second.animateDrawing={5}
will animate the drawing of the arrow in 5 seconds.animateDrawing={0.1}
will animate the drawing of the arrow in 100 milliseconds.
custom svg arrows - headShape and tailShape
new feature! you can customize the svg edges (head or tail) of the arrow. you can use predefined svg by passing
string,one of "arrow1" | "circle" | "heart"
headShape type:string
Code | Result |
---|---|
<xarrow headShape='circle'/> |
|
<xarrow headShape='circle'
arrowHeadProps={"fill": "transparent",
"strokeWidth": "0.1",
"stroke": "CornflowerBlue"}
/> |
|
<xarrow headShape='heart'/> |
you can import arrowShapes
which is object contains all predefined svg shapes.
custom usage
you can also pass your own svg shapes:
headShapeType = {
svgElem: T
:
'circle' | 'ellipse' | 'line' | 'path' | 'polygon' | 'polyline' | 'rect';
svgProps ? : JSX.IntrinsicElements[T];
offsetForward ? : number;
}
;
for example, you can pass the following object, and it will be exactly equivalent to passing 'arrow1'
:
headShape = {
svgElem: 'path',
svgProps: {d: `M 0 0 L 1 0.5 L 0 1 L 0.25 0.5 z`},
offsetForward: 0.25
}
svgElem
- an svg element like path
or circle
.
svgProps
- props that will be passed to the svg element.
offsetForward
- how much to offset tht line into the svg element(from 0 to 1). normally the line will connect to the
start of the svgElem. for example in case of the default arrow you want the line to enter 25% into the svgElem.
don't forget about arrowHeadProps
and arrowTailProps
in case you want to use default shape but custom svg props.
in case you pass a custom svg element: currently you have to adjust the path to start from 0,0 and to be at size box 1x1 in order to make the custom shape look like the default shapes in size, in later versions it is planned to support automatic adjustment using getBBox() function.
The xarrow is fully customizable, and you can pass props to any part of the component. if unlisted(unknown) property is
passed to xarrow so by default it'll be passed down to divContainer
.
you can pass properties to visible parts(body and head) of the arrow (such event handlers and much more). this supposed to be enough for most cases. examples:
passProps= {{onClick: () => console.log("xarrow clicked!")}}
- now the arrow will console log a message when clicked.passProps= {{cursor: "pointer"}}
- now the cursor will change to pointer style when hovering over Xarrow.passProps= {{pointerEvents: "none"}}
- now the user cannot interact with Xarrow via mouse events.
The properties below can be used to customize the arrow even farther:
if you wish you can pass props specific part of the component.
divContainerProps
- the container which contains the SVG canvas, and the optional labels elements. It takes no place, and located where you normaly placed him in the elements tree(no offset). The SVGcanvas and the labels will be placed in a offset from this div.SVGcanvasProps
- the svg canvas which contains arrow head and body.arrowBodyProps
- the body of the arrowarrowHeadProps
- the arrow head.
Note that arrowBody
and arrowHead
receives props of svg path element, SVGcanvas
receives props of svg element,
and divContainerProps
of a div element.
examples:
arrowHead = {onClick: () => console.log("head clicked!")}
- now only the head will console log a message when clicked.
if you wish to pass style to divContainer or SVGcanvas use SVGcanvasStyle
,divContainerStyle
and not SVGcanvasProps
,divContainerProps
to not override existing style.
will extend the svg canvas at all sides. can be useful if for some reason the arrow is cut though to small svg canvas(
should be used in advanced custom arrows). example: _extendSVGcanvas = {30}
- will extend svg canvas in all sides by
30 pixels.
now you can manipulate and offset the control points of the arrow. this way you can control how the line curves. check out the interactive codesandbox, set _debug to true and play with these properties.
If you/your company are using this project, and you want to contribute to his development, please consider donating.
Any donation will help me to devote more time to the development of this project.
See CHANGELOG.md in this repo.
<style> details { border: 1px solid #aaa; border-radius: 4px; padding: .5em .5em 0; margin: 1em 0; } </style>