r3form
v0.3.14
Published
Build immersive, modular 3D forms in React with r3form — powered by react-three-fiber.
Maintainers
Readme
R3Form
A React Three Fiber-based 3D form system for interactive UI elements in 3D space.

R3Form allows you to build 3D forms with interactive inputs, dynamic borders, camera targeting, and smooth hover/press animations.
GitHub Repo
Features
- Fully 3D interactive form fields with text or switch input.
- Structured form data is created automatically on submit.
- Dynamic borders that respond to hover, press, and active states.
- Smooth color and position animations using
lerp. - Automatic camera targeting when a field is focused.
- Compatible with
@react-three/fiberand@react-three/drei.
Installation
npm install r3form
# or
yarn add r3formUsage
Click below to watch the video guide, or follow the steps below:
Import required components from r3form.
import { R3Form, Input3D, SubmitButton } from "r3form";Inside your component, use the R3Form elements:
function App() {
return (
<R3Form onSubmit={(vals) => submitForm(vals)}>
<Input3D name="First Name" />
<Input3D name="Surname" />
<SubmitButton type="Submit">
Submit
</SubmitButton>
</R3Form>
);
}Structured form values are automatically passed to the onSubmit callback as an object:
function submitForm(vals) {
}Example:
{
"First Name": "Tom",
"Surname": "H"
}Components
<R3Form />
The main container for your 3D form. Provides context for inputs, manages camera targeting, styling, and handles submission.
Props:
children(ReactNode) – The form fields/components (e.g.,<Input3D />,<Button3D />).onSubmit(Function) – Callback that receives the form values object when submitting the form.width(string || number, default"100%") – Width of the canvas container.height(string || number, default100%) – Height of the canvas container.cameraPosition(string, default"front") – Camera position relative to the form (e.g.,"bottom-right","top-left","left","right", etc...).shadows(boolean, defaulttrue) – Enable or disable shadows in the 3D scene.background(boolean, defaulttrue) – Enable or disable the visibility of the form background.formColor(string, default"#ffd700") – Colour of the form background.backgroundColor(string, default"#defdfe") – Background colour of the scene. Can use hex with alpha for opacity.formWidth(number, default6) – Width of the 3D form background in scene units.formHeight(number, default6) – Height of the 3D form background in scene units.padding(number, default1) – Padding around the form background in 3D space.gap(number, default0.5) – Vertical gap between form elements in 3D space.zoom(number, default1) – Zoom factor for calculating camera distance from the form.borderColor(string, default"#000000") – Default colour of the form borders.borderColorHover(string, default"#ffffff") – Border colour when hovering over a form element.borderColorActive(string, default"#ffffff") – Border colour when an element is active or being dragged.borderEmissionHoverColor(string, default"#444444") – Emission colour of the border when hovered.borderEmissionActiveColor(string, default"#ffffff") – Emission colour of the border when active/dragged.labelColor(string, default"#000000") – Color of the form labels.
Usage Example:
<R3Form width="100%" height="200px" onSubmit={(vals) => console.log(vals)}>
...
</R3Form><Input3D />
A 3D interactive input field.
Props:
name(string) – Unique name of the input field. Stores in the object handed back from onSubmit.width(number, default6) – Width of the field.height(number, default1) – Height of the field.borderWidth(number, default0.05) – Thickness of the field border.borderDepth(number, default0.1) – Z-depth of the border.hoverDepth(number, default0.1) – Z-offset applied on hover.showFieldBackground(boolean, defaultfalse) – Whether to display a background plane behind the input.elementStyles(object) - Created in R3Form component - handles styling for border and label colours.
<Input3D name="Username" />
<Input3D name="Password" />
<Switch3D />
A 3D interactive toggle switch component.
Props:
name(string) – Unique name of the switch. Value is stored in the object handed back fromonSubmit.position(array, default[0, 0, 0]) –[x, y, z]position of the switch in 3D space.width(number, default6) – Total width of the switch including label and borders.switchWidth(number, default1.2) – Width of the switch track.height(number, default0.5) – Height of the switch track.borderWidth(number, default0.05) – Thickness of the track borders.borderDepth(number, default0.1) – Z-depth of the borders.toggleWidth(number, default0.6) – Width of the toggle handle.labelSize(number, default0.3) – Font size of the switch label.elementStyles(object) – Styling for borders, hover, active states, and label color. Should contain:borderColor(string, default"#000000") – Default border color.borderColorHover(string, default"#ffffff") – Border color when hovering.borderColorActive(string, default"#ffffff") – Border color when active.borderEmissionHoverColor(string, default"#444444") – Emission color of the border on hover.borderEmissionActiveColor(string, default"#ffffff") – Emission color of the border when active/toggled.labelColor(string, default"#000000") – Color of the switch label.
Behavior:
- Click the switch to toggle between
trueandfalse. - Toggle handle animates smoothly between on/off positions.
- Borders glow on hover and when active.
- Track color changes to indicate enabled state.
- Value is stored in the form context object and normalized to
true/false.
Example Usage:
<Switch3D
name="EnableSound"
width={6}
switchWidth={1.5}
height={0.5}
toggleWidth={0.6}
labelSize={0.3}
elementStyles={{
borderColor: "#000000",
borderColorHover: "#ffffff",
borderColorActive: "#ffffff",
borderEmissionHoverColor: "#444444",
borderEmissionActiveColor: "#ffffff",
labelColor: "#000000"
}}
/>
<Switch3D
name="DarkMode"
width={5}
switchWidth={1.2}
height={0.5}
toggleWidth={0.5}
labelSize={0.25}
elementStyles={{
borderColor: "#222222",
borderColorHover: "#ff0000",
borderColorActive: "#00ff00",
borderEmissionHoverColor: "#888888",
borderEmissionActiveColor: "#ffffff",
labelColor: "#000000"
}}
/>
---
#### `<Slider3D />`
A 3D interactive slider/knob component.
**Props:**
- `name` (string) – Unique name of the slider. Value is stored in the object handed back from `onSubmit`.
- `width` (number, default `4`) – Width of the slider rail.
- `labelSize` (number, default `0.3`) – Font size of the slider label.
- `elementStyles` (object) – Handles styling for borders, hover, active states, and label colors. Should contain:
- `borderColor` (string, default `"#000000"`) – Default rail/knob border color.
- `borderColorHover` (string, default `"#ffffff"`) – Border color when hovering over the knob.
- `borderColorActive` (string, default `"#ffffff"`) – Border color when dragging/active.
- `borderEmissionHoverColor` (string, default `"#444444"`) – Emission color of the border on hover.
- `borderEmissionActiveColor` (string, default `"#ffffff"`) – Emission color of the border when active/dragged.
- `labelColor` (string, default `"#000000"`) – Color of the slider label.
**Behavior:**
- Click and drag the knob to change the value.
- Hovering over the knob highlights the torus border.
- Dragging/active state illuminates the knob.
- Value is normalized between 0 and 1 and stored in the form context.
**Example Usage:**
```jsx
<Slider3D
name="Volume"
width={6}
labelSize={0.35}
elementStyles={{
borderColor: "#000000",
borderColorHover: "#ffffff",
borderColorActive: "#ffffff",
borderEmissionHoverColor: "#444444",
borderEmissionActiveColor: "#ffffff",
labelColor: "#000000"
}}
/>
<Slider3D
name="Brightness"
width={4}
labelSize={0.3}
elementStyles={{
borderColor: "#222222",
borderColorHover: "#ff0000",
borderColorActive: "#00ff00",
borderEmissionHoverColor: "#888888",
borderEmissionActiveColor: "#ffffff",
labelColor: "#000000"
}}
/>
---
#### `<SubmitButton />`
A 3D interactive button.
**Props:**
- `width` (number, optional) – Width of the button. Defaults to BaseButton default if not provided.
- `height` (number, optional) – Height of the button. Defaults to BaseButton default if not provided.
- `depth` (number, optional) – Depth (Z-size) of the button. Defaults to BaseButton default if not provided.
- `radius` (number, optional) – Corner radius of the button. Defaults to BaseButton default if not provided.
- `smoothness` (number, optional) – Number of segments for the rounded corners. Defaults to BaseButton default if not provided.
- `color` (string, optional, default `"#00cc88"`) – Button color when not hovered.
- `hoverColor` (string, optional, default `"#00ffaa"`) – Button color on hover.
- `pressDepth` (number, optional) – Z-offset applied when the button is pressed. Defaults to BaseButton default.
- `label` (string, optional, default `"Submit"`) – Text displayed on the button.
- `fontSize` (number, optional) – Size of the text label. Defaults to BaseButton default if not provided.
- `heightPos` (number, optional, default `0`) – Vertical position of the button in the 3D scene.
```jsx
<SubmitButton>Submit</SubmitButton>License
MIT © Tom H
Notes
- Designed to work with stacked 3D elements; calculates height position and gaps for elements from form height.
- Supports smooth hover and press animations with
lerpfor performance-friendly interactions. - Integrates seamlessly with
@react-three/fiberand@react-three/drei.
Contribute
Feel free to help with this project and become a contributor! Please fork the repo and add new 3D form elements, following the structure of the current elements. If you find this package helpful then please help it grow by giving it a star on GitHub!
Check out the GitHub Repo

