diff --git a/packages/core/src/components/vector-range/index.jsx b/packages/core/src/components/vector-range/index.jsx
new file mode 100644
index 0000000..c93c932
--- /dev/null
+++ b/packages/core/src/components/vector-range/index.jsx
@@ -0,0 +1,104 @@
+import './index.scss';
+
+import {
+ PropTypes,
+ React,
+ useState,
+} from '@latus/react';
+
+import Vector from '../vector';
+
+const VectorRange = ({
+ integer,
+ onChange,
+ range,
+}) => {
+ const [isSingle, setIsSingle] = useState(Array.isArray(range));
+ return (
+
+ {
+ onChange?.(
+ isSingle
+ ? value
+ : {
+ min: value,
+ max: range.max,
+ },
+ );
+ }}
+ vector={isSingle ? range : range.min}
+ />
+
+ {!isSingle && (
+ {
+ onChange?.({
+ min: range.min,
+ max: value,
+ });
+ }}
+ vector={range.max}
+ />
+ )}
+
+ );
+};
+
+const createVectorRangePropType = (isRequired) => (props, propName, componentName) => {
+ const fail = (why) => new Error(
+ `Invalid prop ${propName} suppied to ${componentName}. ${why}.`,
+ );
+ const range = props[propName];
+ if (!range && isRequired) {
+ return fail('Is required but missing');
+ }
+ if (
+ (
+ 'undefined' === typeof range.min
+ || 'undefined' === typeof range.max
+ )
+ && (
+ !Array.isArray(range)
+ || 2 !== range.length
+ )
+ ) {
+ return fail('Expected {min, max} or a Vector');
+ }
+ return undefined;
+};
+export const vectorRangePropType = createVectorRangePropType(false);
+vectorRangePropType.isRequired = createVectorRangePropType(true);
+
+VectorRange.defaultProps = {
+ integer: false,
+ onChange: null,
+};
+
+VectorRange.propTypes = {
+ integer: PropTypes.bool,
+ onChange: PropTypes.func,
+ range: vectorRangePropType.isRequired,
+};
+
+export default VectorRange;
diff --git a/packages/core/src/components/vector-range/index.scss b/packages/core/src/components/vector-range/index.scss
new file mode 100644
index 0000000..a774dc9
--- /dev/null
+++ b/packages/core/src/components/vector-range/index.scss
@@ -0,0 +1,11 @@
+.vector-range {
+ display: flex;
+}
+
+.vector-range__to-label {
+ align-self: center;
+}
+
+.vector-range input[type="number"] {
+ margin-left: 1em;
+}
diff --git a/packages/core/src/components/vector/index.jsx b/packages/core/src/components/vector/index.jsx
new file mode 100644
index 0000000..5452d51
--- /dev/null
+++ b/packages/core/src/components/vector/index.jsx
@@ -0,0 +1,74 @@
+import './index.scss';
+
+import {PropTypes, React} from '@latus/react';
+import Number from '../number';
+
+const Vector = ({
+ integer,
+ labels,
+ onChange,
+ vector,
+}) => (
+
+
+
+
+);
+
+const createVectorPropType = (isRequired) => (props, propName, componentName) => {
+ const fail = (why) => new Error(
+ `Invalid prop ${propName} suppied to ${componentName}. ${why}.`,
+ );
+ const vector = props[propName];
+ if (!vector && isRequired) {
+ return fail('Is required but missing');
+ }
+ if (
+ !Array.isArray(vector)
+ || 2 !== vector.length
+ ) {
+ return fail('Expected 2-length array vector');
+ }
+ return undefined;
+};
+export const vectorPropType = createVectorPropType(false);
+vectorPropType.isRequired = createVectorPropType(true);
+
+Vector.defaultProps = {
+ integer: false,
+ onChange: null,
+ labels: ['X', 'Y'],
+};
+
+Vector.propTypes = {
+ integer: PropTypes.bool,
+ onChange: PropTypes.func,
+ labels: PropTypes.arrayOf(PropTypes.string),
+ vector: vectorPropType.isRequired,
+};
+
+export default Vector;
diff --git a/packages/core/src/components/vector/index.scss b/packages/core/src/components/vector/index.scss
new file mode 100644
index 0000000..56084b0
--- /dev/null
+++ b/packages/core/src/components/vector/index.scss
@@ -0,0 +1,3 @@
+.vector {
+ display: flex;
+}
diff --git a/packages/core/src/index.js b/packages/core/src/index.js
index 7d165d8..8f2a298 100644
--- a/packages/core/src/index.js
+++ b/packages/core/src/index.js
@@ -2,6 +2,8 @@ import {projects, user} from './state';
export {default as Number} from './components/number';
export {default as Range, rangePropType} from './components/range';
+export {default as Vector, vectorPropType} from './components/vector';
+export {default as VectorRange, vectorRangePropType} from './components/vector-range';
export {default as ProjectContext} from './context/project';
export {default as UriContext} from './context/uri';
export {default as useProject} from './hooks/use-project';