diff --git a/src/App.js b/src/App.js
index 5a73669c8db2cfe8fc7682998c3ca2d7d45c8697..bb9243362c897ace4f74d615b2ad6371c1094c18 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,5 +1,6 @@
 import React from "react";
 import { BrowserRouter, Switch, Route } from "react-router-dom";
+import MemberAdder from "./MemberAdder";
 import MemberList from "./MemberList";
 import ProductList from "./ProductList";
 import Dashboard from "./Dashboard";
@@ -19,6 +20,9 @@ function Panel(props) {
       <BrowserRouter>
         <Head onLogout={props.onLogout} />
         <Switch>
+          <Route path="/members/add">
+            <MemberAdder />
+          </Route>
           <Route path="/members">
             <MemberList />
           </Route>
diff --git a/src/Head.js b/src/Head.js
index 740778cf1d21dc11051a39cbee88ec2e9184810c..181d5583f72bd3387ee8f2c07df01c078e9eea6b 100644
--- a/src/Head.js
+++ b/src/Head.js
@@ -14,6 +14,7 @@ function Head(props) {
       <NavDropdown title="Admin" id="admin">
         <Nav.Link href="/members">Socias</Nav.Link>
         <Nav.Link href="/topup">Recarga</Nav.Link>
+        <Nav.Link href="/members/add">Nueva socia</Nav.Link>
       </NavDropdown>
     );
   }
diff --git a/src/MemberAdder.js b/src/MemberAdder.js
new file mode 100644
index 0000000000000000000000000000000000000000..5a2799729f288b5fab2406be9f444b8234aca766
--- /dev/null
+++ b/src/MemberAdder.js
@@ -0,0 +1,192 @@
+import React from "react";
+import { Redirect } from "react-router-dom";
+import {
+  Container,
+  Form,
+  Alert,
+  Button,
+  InputGroup,
+  Spinner,
+} from "react-bootstrap";
+import AuthContext from "./AuthContext";
+
+class MemberAdder extends React.Component {
+  static contextType = AuthContext;
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      num: null,
+      login: null,
+      name: null,
+      email: null,
+      phone: null,
+      password: null,
+      euros: 0,
+      cents: 0,
+      admin: false,
+      error: null,
+      isLoading: false,
+      redirect: false,
+    };
+  }
+
+  send(e) {
+    e.preventDefault();
+
+    if (!this.state.num || !this.state.login || !this.state.password) {
+      this.setState({
+        error:
+          "El numero, nombre de acceso y contraseña no pueden estar vacios",
+      });
+      return;
+    }
+    this.setState({ isLoading: true, error: null });
+
+    const member = {
+      num: parseInt(this.state.num),
+      login: this.state.login,
+      name: this.state.name,
+      email: this.state.email,
+      phone: this.state.phone,
+      password: this.state.password,
+      balance: parseInt(this.state.euros) * 100 + parseInt(this.state.cents),
+      role: this.state.admin ? "admin" : "member",
+    };
+
+    const body = JSON.stringify(member);
+    fetch("/api/member", {
+      headers: { "x-authentication": this.context.token },
+      method: "POST",
+      body,
+    }).then((response) => {
+      if (response.ok) {
+        this.setState({
+          isLoading: false,
+          redirect: true,
+        });
+      } else {
+        this.setState({
+          isLoading: false,
+          error:
+            "Ha ocurrido un problema creando la socia: " +
+            response.status.toString() +
+            " " +
+            response.statusText,
+        });
+      }
+    });
+  }
+
+  render() {
+    if (this.state.redirect) {
+      return <Redirect to="/members" />;
+    }
+
+    if (this.state.isLoading) {
+      return <Spinner animation="border" />;
+    }
+
+    let alert;
+    if (this.state.error) {
+      alert = <Alert variant="danger">{this.state.error}</Alert>;
+    }
+
+    return (
+      <Container>
+        {alert}
+        <Form onSubmit={(e) => this.send(e)}>
+          <Form.Group>
+            <Form.Label>Numero de socia:</Form.Label>
+            <Form.Control
+              type="number"
+              placeholder="numero"
+              value={this.state.num}
+              onChange={(e) => this.setState({ num: e.target.value })}
+              isInvalid={!this.state.num}
+            />
+          </Form.Group>
+          <Form.Group>
+            <Form.Label>Nombre de acceso:</Form.Label>
+            <Form.Control
+              placeholder="login"
+              value={this.state.login}
+              onChange={(e) => this.setState({ login: e.target.value })}
+              isInvalid={!this.state.login}
+            />
+          </Form.Group>
+          <Form.Group>
+            <Form.Label>Nombre:</Form.Label>
+            <Form.Control
+              placeholder="nombre"
+              value={this.state.name}
+              onChange={(e) => this.setState({ name: e.target.value })}
+            />
+          </Form.Group>
+          <Form.Group>
+            <Form.Label>Email:</Form.Label>
+            <Form.Control
+              type="email"
+              placeholder="email"
+              value={this.state.email}
+              onChange={(e) => this.setState({ email: e.target.value })}
+            />
+          </Form.Group>
+          <Form.Group>
+            <Form.Label>Telefono:</Form.Label>
+            <Form.Control
+              placeholder="telefono"
+              value={this.state.phone}
+              onChange={(e) => this.setState({ phone: e.target.value })}
+            />
+          </Form.Group>
+          <Form.Group>
+            <Form.Label>Contraseña:</Form.Label>
+            <Form.Control
+              type="password"
+              placeholder="contraseña"
+              value={this.state.password}
+              onChange={(e) => this.setState({ password: e.target.value })}
+              isInvalid={!this.state.password}
+            />
+          </Form.Group>
+          <Form.Group>
+            <Form.Check
+              type="switch"
+              id="admin"
+              label="Es administradora"
+              checked={this.state.admin}
+              onChange={(e) => this.setState({ admin: e.target.checked })}
+            />
+          </Form.Group>
+          <Form.Group>
+            <Form.Label>Saldo inicial:</Form.Label>
+            <InputGroup>
+              <Form.Control
+                placeholder="euros"
+                value={this.state.euros}
+                onChange={(e) => this.setState({ euros: e.target.value })}
+              />
+              <InputGroup.Append>
+                <InputGroup.Text>.</InputGroup.Text>
+              </InputGroup.Append>
+              <Form.Control
+                placeholder="centimos"
+                value={this.state.cents}
+                onChange={(e) => this.setState({ cents: e.target.value })}
+                min="0"
+                max="99"
+              />
+              <InputGroup.Append>
+                <InputGroup.Text>€</InputGroup.Text>
+              </InputGroup.Append>
+            </InputGroup>
+          </Form.Group>
+          <Button type="submit">Crear usuaria</Button>
+        </Form>
+      </Container>
+    );
+  }
+}
+
+export default MemberAdder;
diff --git a/src/MemberList.js b/src/MemberList.js
index eb3bd39e17999624c0a5f1ffe1dcd41dd77dabcc..8a5c3f5700bd965bdcdb33975b0d20c0b645fe83 100644
--- a/src/MemberList.js
+++ b/src/MemberList.js
@@ -67,25 +67,6 @@ class MemberList extends React.Component {
     });
   }
 
-  addMember(member) {
-    let members = this.state.members;
-    members.push(member);
-    this.setState({ members });
-
-    const body = JSON.stringify(member);
-    fetch("/api/member", {
-      headers: { "x-authentication": this.context.token },
-      method: "POST",
-      body,
-    }).then((response) => {
-      if (!response.ok) {
-        this.setState({
-          error: response.status.toString() + " " + response.statusText,
-        });
-      }
-    });
-  }
-
   render() {
     let alert = null;
     if (this.state.error !== null) {
@@ -140,7 +121,6 @@ class MemberList extends React.Component {
       );
     });
 
-    // TODO: add member
     return (
       <Fetcher
         url="/api/member"